diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index e94fbda3..07783783 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -28,6 +28,6 @@ jobs: - name: connect and update traefik run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ vars.PROD_DIR }} && docker compose --compatibility -p ows-events -f docker-compose.traefik.yml up -d --build && exit" - name: connect and rebuild services - run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "export GITHUB_PARSING_TOKEN=${{ secrets.PARSING_TOKEN }} && export SECRET_KEY=${{ secrets.BACKEND_SECRET_KEY }} && export VITE_TELEGRAM_AUTH_BOT_NAME=${{ vars.PROD_AUTH_TELEGRAM_BOT_NAME }} && cd ${{ vars.PROD_DIR }} && docker compose --compatibility -p ows-events_prod -f docker-compose.prod.yml up -d --force-recreate --build && exit" + run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "export VITE_GTAG_ID=${{ secrets.PROD_GTAG_ID }} && export GITHUB_PARSING_TOKEN=${{ secrets.PARSING_TOKEN }} && export SECRET_KEY=${{ secrets.BACKEND_SECRET_KEY }} && export VITE_TELEGRAM_AUTH_BOT_NAME=${{ vars.PROD_AUTH_TELEGRAM_BOT_NAME }} && cd ${{ vars.PROD_DIR }} && docker compose --compatibility -p ows-events_prod -f docker-compose.prod.yml up -d --force-recreate --build && exit" - name: cleanup run: rm -rf ~/.ssh diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index cae4cfb2..c08f0ab5 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -28,6 +28,6 @@ jobs: - name: connect and update traefik run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ vars.TEST_DIR }} && docker compose --compatibility -p ows-events -f docker-compose.traefik.yml up -d --build && exit" - name: connect and rebuild services - run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "export GITHUB_PARSING_TOKEN=${{ secrets.PARSING_TOKEN }} && export SECRET_KEY=${{ secrets.BACKEND_SECRET_KEY }} && export VITE_TELEGRAM_AUTH_BOT_NAME=${{ vars.TEST_AUTH_TELEGRAM_BOT_NAME }} && cd ${{ vars.TEST_DIR }} && docker compose --compatibility -p ows-events_test -f docker-compose.test.yml up -d --force-recreate --build && exit" + run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "export VITE_GTAG_ID=${{ secrets.TEST_GTAG_ID }} && export GITHUB_PARSING_TOKEN=${{ secrets.PARSING_TOKEN }} && export SECRET_KEY=${{ secrets.BACKEND_SECRET_KEY }} && export VITE_TELEGRAM_AUTH_BOT_NAME=${{ vars.TEST_AUTH_TELEGRAM_BOT_NAME }} && cd ${{ vars.TEST_DIR }} && docker compose --compatibility -p ows-events_test -f docker-compose.test.yml up -d --force-recreate --build && exit" - name: cleanup run: rm -rf ~/.ssh diff --git a/backend/src/controllers/events-state-controller.ts b/backend/src/controllers/events-state-controller.ts index f2148e43..52280774 100644 --- a/backend/src/controllers/events-state-controller.ts +++ b/backend/src/controllers/events-state-controller.ts @@ -1,15 +1,9 @@ import { v4 as uuid } from 'uuid'; import { FilterQuery } from 'mongoose'; -import { EventDbEntity, EventOnPoster } from '@common/types/event'; +import { EventDbEntity, EventOnPoster, SearchEventPayload } from '@common/types/event'; import { EventModel } from '../models/event.model'; import { imageController } from './image-controller'; -export type FindEventParams = { - searchLine?: string; - city?: string; - country?: string; -}; - class EventsStateController { async addEvent(event: EventOnPoster) { const id = uuid(); @@ -25,7 +19,7 @@ class EventsStateController { return id; } - async getEvents(query?: FindEventParams | undefined): Promise { + async getEvents(query?: SearchEventPayload | undefined): Promise { const queryObject: FilterQuery = {}; if (query?.searchLine) { queryObject.$text = { $search: query.searchLine }; @@ -36,19 +30,32 @@ class EventsStateController { if (query?.city) { queryObject['location.city'] = query?.city; } + if (query?.tags && query?.tags.length !== 0) { + queryObject.tags = { $in: query?.tags }; + } queryObject['meta.moderation.status'] = { $nin: ['declined', 'in-progress'] }; - const futureEvents = await EventModel.find( - { ...queryObject, date: { $gt: Date.now() } }, - {}, + const pipeline = [ { - sort: { - date: 'ascending' + $match: { + ...queryObject, + $expr: { + $gte: [ + { + $add: ['$date', { $multiply: [1000, '$durationInSeconds'] }] + }, + { + $toDouble: '$$NOW' + } + ] + } } } - ).exec(); - - return futureEvents.map((event) => event.toObject()); + ]; + const futureEvents = await EventModel.aggregate(pipeline) + .sort({ date: 'ascending' }) + .exec(); + return futureEvents; } async getEvent(id: string) { @@ -98,8 +105,8 @@ class EventsStateController { return event; } - async findAllTags() { - const tags = await EventModel.distinct('tags'); + async findUsedTags() { + const tags = await EventModel.distinct('tags', { date: { $gt: Date.now() } }); return tags; } diff --git a/backend/src/rest/v1/auth/router.ts b/backend/src/rest/v1/auth/router.ts index dd1d39ef..cd9f27e9 100644 --- a/backend/src/rest/v1/auth/router.ts +++ b/backend/src/rest/v1/auth/router.ts @@ -1,7 +1,8 @@ import { FastifyInstance } from 'fastify'; import { ITelegramRoute } from './type'; import { telegramLogin } from './controller'; +import { telegramSchema } from './schema'; export const authApi = async (fastify: FastifyInstance) => { - fastify.get('/telegram', telegramLogin); + fastify.get('/telegram', { schema: telegramSchema, handler: telegramLogin }); }; diff --git a/backend/src/rest/v1/auth/schema.ts b/backend/src/rest/v1/auth/schema.ts new file mode 100644 index 00000000..319ac2cd --- /dev/null +++ b/backend/src/rest/v1/auth/schema.ts @@ -0,0 +1,34 @@ +export const telegramSchema = { + description: 'Login/register through telegram', + tags: ['Auth'], + summary: 'Login through telegram', + response: { + 302: { + type: 'null', + description: 'Redirect to special page, where token get stored on client side' + } + }, + querystring: { + type: 'object', + properties: { + id: { + type: 'string' + }, + first_name: { + type: 'string' + }, + last_name: { + type: 'string' + }, + username: { + type: 'string' + }, + photo_url: { + type: 'string' + }, + auth_date: { + type: 'number' + } + } + } +}; diff --git a/backend/src/rest/v1/events/controller.ts b/backend/src/rest/v1/events/controller.ts index 8a368cd9..a362d66b 100644 --- a/backend/src/rest/v1/events/controller.ts +++ b/backend/src/rest/v1/events/controller.ts @@ -107,8 +107,8 @@ export const updateEvent: IUpdateEventHandler = async (request) => { }; export const findEvents: IFindEventHandler = async (request) => { - const { searchLine, country, city } = request.body; + const { searchLine, country, city, tags } = request.body; - const events = await eventsStateController.getEvents({ searchLine, country, city }); + const events = await eventsStateController.getEvents({ searchLine, country, city, tags }); return events; }; diff --git a/backend/src/rest/v1/events/schema.ts b/backend/src/rest/v1/events/schema.ts index 61195e86..ed5b7b90 100644 --- a/backend/src/rest/v1/events/schema.ts +++ b/backend/src/rest/v1/events/schema.ts @@ -164,7 +164,8 @@ export const findEventsSchema = { properties: { searchLine: { type: 'string' }, city: { type: 'string' }, - country: { type: 'string' } + country: { type: 'string' }, + tags: { type: 'array', items: { type: 'string' } } } }, security: [{ authJWT: [] }] diff --git a/backend/src/rest/v1/events/type.d.ts b/backend/src/rest/v1/events/type.d.ts index d22a8815..4ee0a788 100644 --- a/backend/src/rest/v1/events/type.d.ts +++ b/backend/src/rest/v1/events/type.d.ts @@ -1,7 +1,6 @@ import { EventOnPoster } from '@common/types'; -import { EventParams } from '@common/types/event'; +import { EventParams, SearchEventPayload } from '@common/types/event'; import { SupportedLanguages } from '../../../../../common/const'; -import { FindEventParams } from '../../../controllers/events-state-controller'; import { IRouteHandler } from '../../types'; type IAddEventRoute = { @@ -55,7 +54,7 @@ type IFindEventRoute = { Header: { 'accept-language': SupportedLanguages; }; - Body: FindEventParams; + Body: SearchEventPayload; Reply: EventOnPoster[]; }; type IFindEventHandler = IRouteHandler; diff --git a/backend/src/rest/v1/location/router.ts b/backend/src/rest/v1/location/router.ts index 01c866c9..41325c61 100644 --- a/backend/src/rest/v1/location/router.ts +++ b/backend/src/rest/v1/location/router.ts @@ -11,6 +11,7 @@ import { getCitiesByCountrySchema, getCountriesSchema, getMetaSchema, + getUsedCitiesByCountrySchema, getUsedCountriesSchema } from './schema'; @@ -34,5 +35,8 @@ export const locationApi = async (fastify: FastifyInstance) => { schema: getUsedCountriesSchema, handler: getUsedCountries }); - fastify.get('/usedCities/:country', getUsedCities); + fastify.get('/usedCities/:country', { + schema: getUsedCitiesByCountrySchema, + handler: getUsedCities + }); }; diff --git a/backend/src/rest/v1/location/schema.ts b/backend/src/rest/v1/location/schema.ts index ac1ac15f..0953b0c2 100644 --- a/backend/src/rest/v1/location/schema.ts +++ b/backend/src/rest/v1/location/schema.ts @@ -63,3 +63,21 @@ export const getUsedCountriesSchema = { } } }; + +export const getUsedCitiesByCountrySchema = { + description: 'get used cities by country', + tags: ['Location'], + summary: 'Get used cities by country', + params: { + type: 'object', + properties: { + country: { type: 'string' } + } + }, + response: { + 200: { + type: 'array', + items: { type: 'string' } + } + } +}; diff --git a/backend/src/rest/v1/moderation/router.ts b/backend/src/rest/v1/moderation/router.ts index c351db6d..687a64db 100644 --- a/backend/src/rest/v1/moderation/router.ts +++ b/backend/src/rest/v1/moderation/router.ts @@ -1,11 +1,18 @@ import { FastifyInstance } from 'fastify'; import { approve, decline, get } from './controller'; import { IApproveEventRoute, IDeclineEventRoute, IGetEventsRoute } from './types'; +import { approveEventSchema, declineEventSchema, getEventsByStatusSchema } from './schema'; export const manualModerationApi = async (fastify: FastifyInstance) => { - fastify.get('/get/:status', get); + fastify.get('/get/:status', { schema: getEventsByStatusSchema, handler: get }); - fastify.get('/approve/:eventId', approve); + fastify.get('/approve/:eventId', { + schema: approveEventSchema, + handler: approve + }); - fastify.get('/decline/:eventId', decline); + fastify.get('/decline/:eventId', { + schema: declineEventSchema, + handler: decline + }); }; diff --git a/backend/src/rest/v1/moderation/schema.ts b/backend/src/rest/v1/moderation/schema.ts new file mode 100644 index 00000000..455dab35 --- /dev/null +++ b/backend/src/rest/v1/moderation/schema.ts @@ -0,0 +1,43 @@ +import { ItemEvent } from '../events/schema'; + +export const getEventsByStatusSchema = { + description: 'get events by moderation status', + tags: ['Moderation'], + summary: 'Get events by moderation status', + params: { + type: 'object', + properties: { + country: { type: 'string' } + } + }, + response: { + 200: { + type: 'array', + items: ItemEvent + } + } +}; + +export const approveEventSchema = { + description: 'approve event', + tags: ['Moderation'], + summary: 'Approve event', + params: { + type: 'object', + properties: { + eventId: { type: 'string' } + } + } +}; + +export const declineEventSchema = { + description: 'decline event', + tags: ['Moderation'], + summary: 'Decline event', + params: { + type: 'object', + properties: { + eventId: { type: 'string' } + } + } +}; diff --git a/backend/src/rest/v1/tags/controller.ts b/backend/src/rest/v1/tags/controller.ts index 1e099784..30150afc 100644 --- a/backend/src/rest/v1/tags/controller.ts +++ b/backend/src/rest/v1/tags/controller.ts @@ -1,32 +1,32 @@ -import jwt from 'jsonwebtoken'; +// import jwt from 'jsonwebtoken'; import { CommonErrorsEnum } from '../../../../../common/const'; import { eventsStateController } from '../../../controllers/events-state-controller'; import { - IAddTagHandler, - IGetAllTagsHandler, - IGetTagByEventHandler, - IDeleteTagsHandler + // IAddTagHandler, + IGetUsedTagsHandler, + IGetTagByEventHandler + // IDeleteTagsHandler } from './type'; -import { ITokenData } from '../../types'; -import { vars } from '../../../config/vars'; - -export const addTags: IAddTagHandler = async (request) => { - const { event } = request.body; - if (!event) throw new Error(CommonErrorsEnum.NO_PAYLOAD_PROVIDED); - const token = request.headers.authorization; - if (!token) throw new Error(CommonErrorsEnum.UNAUTHORIZED); - - const jwtData = jwt.verify(token, vars.secret) as ITokenData; - if (!jwtData.id) throw new Error(CommonErrorsEnum.WRONG_TOKEN); - - event.creatorId = jwtData.id; - const response = await eventsStateController.addTags(event); - - return { newtag: response }; -}; - -export const getAllTags: IGetAllTagsHandler = async () => { - const response = await eventsStateController.findAllTags(); +// import { ITokenData } from '../../types'; +// import { vars } from '../../../config/vars'; + +// export const addTags: IAddTagHandler = async (request) => { +// const { event } = request.body; +// if (!event) throw new Error(CommonErrorsEnum.NO_PAYLOAD_PROVIDED); +// const token = request.headers.authorization; +// if (!token) throw new Error(CommonErrorsEnum.UNAUTHORIZED); +// +// const jwtData = jwt.verify(token, vars.secret) as ITokenData; +// if (!jwtData.id) throw new Error(CommonErrorsEnum.WRONG_TOKEN); +// +// event.creatorId = jwtData.id; +// const response = await eventsStateController.addTags(event); +// +// return { newtag: response }; +// }; + +export const getUsedTags: IGetUsedTagsHandler = async () => { + const response = await eventsStateController.findUsedTags(); return response; }; @@ -39,17 +39,17 @@ export const getTagByEventId: IGetTagByEventHandler = async (request) => { return response; }; -export const deleteTag: IDeleteTagsHandler = async (request) => { - const { event } = request.body; - if (!event.tags || !event.id) throw new Error(CommonErrorsEnum.NO_PAYLOAD_PROVIDED); - const token = request.headers.authorization; - if (!token) throw new Error(CommonErrorsEnum.UNAUTHORIZED); - - const jwtData = jwt.verify(token, vars.secret) as ITokenData; - if (!jwtData.id) throw new Error(CommonErrorsEnum.WRONG_TOKEN); - - event.creatorId = jwtData.id; - const response = await eventsStateController.removeTags(event); - - return response; -}; +// export const deleteTag: IDeleteTagsHandler = async (request) => { +// const { event } = request.body; +// if (!event.tags || !event.id) throw new Error(CommonErrorsEnum.NO_PAYLOAD_PROVIDED); +// const token = request.headers.authorization; +// if (!token) throw new Error(CommonErrorsEnum.UNAUTHORIZED); +// +// const jwtData = jwt.verify(token, vars.secret) as ITokenData; +// if (!jwtData.id) throw new Error(CommonErrorsEnum.WRONG_TOKEN); +// +// event.creatorId = jwtData.id; +// const response = await eventsStateController.removeTags(event); +// +// return response; +// }; diff --git a/backend/src/rest/v1/tags/router.ts b/backend/src/rest/v1/tags/router.ts index dfc0c2a8..3a808fa0 100644 --- a/backend/src/rest/v1/tags/router.ts +++ b/backend/src/rest/v1/tags/router.ts @@ -1,28 +1,17 @@ import { FastifyInstance } from 'fastify'; -import { - addTags, - getAllTags, - getTagByEventId, - deleteTag, -} from './controller'; -import { - IAddTagRoute, - IDeleteTagsRoute, - IGetAllTagsRoute, - IGetTagByEventRoute, -} from './type'; -import { - addTagSchema, deleteTagSchema, getAllTagsSchema, getTagByEventSchema, -} from './schema'; - +import { getUsedTags, getTagByEventId } from './controller'; +import { IGetUsedTagsRoute, IGetTagByEventRoute } from './type'; +import { getUsedTagsSchema, getTagByEventSchema } from './schema'; export const tagsApi = async (fastify: FastifyInstance) => { - fastify.post('/add', { schema: addTagSchema, handler: addTags }); - - fastify.get('/', { schema: getAllTagsSchema, handler: getAllTags }); + // fastify.post('/add', { schema: addTagSchema, handler: addTags }); - fastify.get('/:id', { schema: getTagByEventSchema, handler: getTagByEventId }); + fastify.get('/used', { schema: getUsedTagsSchema, handler: getUsedTags }); - fastify.post('/delete', { schema: deleteTagSchema, handler: deleteTag }); + fastify.get('/:id', { + schema: getTagByEventSchema, + handler: getTagByEventId + }); + // fastify.post('/delete', { schema: deleteTagSchema, handler: deleteTag }); }; diff --git a/backend/src/rest/v1/tags/schema.ts b/backend/src/rest/v1/tags/schema.ts index 72d7b853..f8c58dca 100644 --- a/backend/src/rest/v1/tags/schema.ts +++ b/backend/src/rest/v1/tags/schema.ts @@ -1,4 +1,4 @@ -import { ItemEvent } from "../events/schema"; +import { ItemEvent } from '../events/schema'; export const addTagSchema = { description: 'Add new tag/s', @@ -17,10 +17,10 @@ export const addTagSchema = { security: [{ authJWT: [] }] }; -export const getAllTagsSchema = { - description: 'get all tags', +export const getUsedTagsSchema = { + description: 'get used tags', tags: ['Tags'], - summary: 'Get all tags', + summary: 'Get used tags', response: { 200: { type: 'array', @@ -41,12 +41,12 @@ export const getTagByEventSchema = { }, params: { type: 'object', - properties: { - id: { - type: 'string', - description: 'Event id' - } - } + properties: { + id: { + type: 'string', + description: 'Event id' + } + } } }; diff --git a/backend/src/rest/v1/tags/type.d.ts b/backend/src/rest/v1/tags/type.d.ts index de014892..ab968295 100644 --- a/backend/src/rest/v1/tags/type.d.ts +++ b/backend/src/rest/v1/tags/type.d.ts @@ -8,10 +8,10 @@ type IAddTagRoute = { }; type IAddTagHandler = IRouteHandler; -type IGetAllTagsRoute = { - Reply: string[]; +type IGetUsedTagsRoute = { + Reply: string[]; }; -type IGetAllTagsHandler = IRouteHandler; +type IGetUsedTagsHandler = IRouteHandler; type IDeleteTagsRoute = { Body: { event: EventOnPoster }; @@ -21,8 +21,7 @@ type IDeleteTagsRoute = { type IDeleteTagsHandler = IRouteHandler; type IGetTagByEventRoute = { - Params: { id: string }; - Reply: string[]; + Params: { id: string }; + Reply: string[]; }; type IGetTagByEventHandler = IRouteHandler; - diff --git a/common/const/tags.ts b/common/const/tags.ts new file mode 100644 index 00000000..3127563b --- /dev/null +++ b/common/const/tags.ts @@ -0,0 +1,22 @@ +export enum Tags { + CONFERENCE = 'conference', + FESTIVAL = 'festival', + EXHIBITION = 'exhibition', + THEATER = 'theater', + STANDUP = 'standup', + EXCURSION = 'excursion', + LECTURE = 'lecture', + PARTY = 'party', + MASTERCLASS = 'masterclass', + SPORT = 'sport', + TOURISM = 'tourism', + HOLIDAY = 'holiday', + CONCERT = 'concert', + CHILDREN = 'children', + ADULT = 'adult' +} + +export const TagsArray: string[] = Object.values(Tags); + +export type TagList = typeof TagsArray; +export type Tag = (typeof TagsArray)[number]; diff --git a/common/types/event.ts b/common/types/event.ts index 14aa78dc..bd0a7576 100644 --- a/common/types/event.ts +++ b/common/types/event.ts @@ -1,5 +1,6 @@ import type { Timezone } from './location'; import { EventTypes } from '../const/eventTypes'; +import { type Tag, Tags } from '../const/tags'; export type EventDbEntity = { id: string; @@ -18,7 +19,7 @@ export type EventDbEntity = { timezone?: Timezone; url: string; organizer?: string; - tags?: string[]; + tags?: Tag[]; type: EventTypes; }; @@ -47,6 +48,7 @@ export type SearchEventPayload = { searchLine?: string; country?: string; city?: string; + tags?: Tags[]; }; export type EventPrice = { diff --git a/common/types/location.ts b/common/types/location.ts index 5e1c1200..4046c09b 100644 --- a/common/types/location.ts +++ b/common/types/location.ts @@ -8,3 +8,6 @@ export type Timezone = { timezoneName: string; timezoneOffset: string; }; + +export type Country = string; +export type City = string; \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 44cbfff7..5ac35b49 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -24,6 +24,7 @@ services: - VITE_TELEGRAM_AUTH_BOT_NAME - VITE_MODE=production - VITE_DOMAIN=afisha.peredelano.com + - VITE_GTAG_ID logging: *default-logging restart: always labels: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 4d707d3d..fc818893 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -24,6 +24,7 @@ services: - VITE_TELEGRAM_AUTH_BOT_NAME - VITE_MODE=production - VITE_DOMAIN=test.afisha.peredelano.com + - VITE_GTAG_ID logging: *default-logging restart: always labels: diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index bf4bb02b..691769e7 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -13,7 +13,7 @@ module.exports = { tsconfigRootDir: __dirname, ecmaVersion: 'latest' }, - ignorePatterns: ['./node_modules/*', './build/*', './dist/*', './assets/*'], + ignorePatterns: ['./node_modules/*', './build/*', './dist/*', './assets/*', "modules/1compileI18n/index.ts"], rules: { 'vue/html-self-closing': 'off', 'vue/multi-word-component-names': 'off', diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 70c3301e..75b02198 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -19,6 +19,7 @@ ARG VITE_POSTAUTH_URL ARG VITE_TELEGRAM_AUTH_BOT_NAME ARG VITE_MODE ARG VITE_DOMAIN +ARG VITE_GTAG_ID ENV NODE_ENV ${NODE_ENV} ENV VITE_API_URL ${VITE_API_URL} @@ -29,6 +30,7 @@ ENV VITE_POSTAUTH_URL ${VITE_POSTAUTH_URL} ENV VITE_TELEGRAM_AUTH_BOT_NAME ${VITE_TELEGRAM_AUTH_BOT_NAME} ENV VITE_MODE ${VITE_MODE} ENV VITE_DOMAIN ${VITE_DOMAIN} +ENV VITE_GTAG_ID ${VITE_GTAG_ID} RUN npm run postinstall RUN npm run build-demo diff --git a/frontend/app.vue b/frontend/app.vue index 9e995ab1..12d9b36d 100644 --- a/frontend/app.vue +++ b/frontend/app.vue @@ -4,8 +4,10 @@ import 'vue-final-modal/style.css'; import 'vue-toastification/dist/index.css'; import 'virtual:svg-icons-register'; import { ModalsContainer } from 'vue-final-modal'; -import { CookieNameEnum } from './constants/enums/common'; +import { CookieNameEnum, LocalStorageEnum } from './constants/enums/common'; import { TOKEN_MAX_AGE_SECONDS } from './constants/defaultValues/time'; +import type { ComputedRef } from 'vue'; +import { getRouteName } from './utils'; const { locale, t } = useI18n(); @@ -13,13 +15,13 @@ const viewport = useViewport(); const mobile = computed(() => viewport.isLessThan('tablet')); const tablet = computed( - () => viewport.isGreaterOrEquals('tablet') || viewport.isLessThan('desktop') + () => viewport.isGreaterOrEquals('tablet') && viewport.isLessThan('desktop') ); const desktop = computed(() => viewport.isGreaterOrEquals('desktop')); -provide('mobile', mobile); -provide('tablet', tablet); -provide('desktop', desktop); +provide('mobile', mobile as ComputedRef); +provide('tablet', tablet as ComputedRef); +provide('desktop', desktop as ComputedRef); useHead({ title: t('meta.default_title'), @@ -33,8 +35,16 @@ useHead({ } }); +const route = useRoute(); +const { gtag } = useGtag(); +gtag('event', 'page_view', { + screen_name: () => getRouteName(route.name as string) +}); + useCookie(CookieNameEnum.LOCALE, { maxAge: TOKEN_MAX_AGE_SECONDS }).value = locale.value; +//TODO ALL_TIMEZONES перенесены в sessionStorage, ниже функция для очистки их в localStorage у пользователей +onMounted(() => localStorage.removeItem(LocalStorageEnum.TIMEZONES));