diff --git a/src/api/request.ts b/src/api/request.ts index 1c1a855..62274a0 100644 --- a/src/api/request.ts +++ b/src/api/request.ts @@ -1,5 +1,15 @@ +import router, { PAGE, PAGE_URL } from '@/router'; import axios from 'axios'; export const requestApi = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, }) + +requestApi.interceptors.response.use(null, async (error) => { + if (error.response.status === 401) { + const url = PAGE_URL[PAGE.AUTH] + '?message=auth-required'; + await router.push(url) + } + + return Promise.reject(error); +}); diff --git a/src/components/request/RequestFilter.vue b/src/components/request/RequestFilter.vue new file mode 100644 index 0000000..e5b6d91 --- /dev/null +++ b/src/components/request/RequestFilter.vue @@ -0,0 +1,66 @@ + + + + + + + + + + Выберите статус + + + Завершен + + + Отменен + + + Активен + + + Выполняется + + + + + + Очистить + + + + + + + diff --git a/src/components/request/RequestForm.vue b/src/components/request/RequestForm.vue index 22d118f..2959762 100644 --- a/src/components/request/RequestForm.vue +++ b/src/components/request/RequestForm.vue @@ -68,16 +68,16 @@ v-model="status" :disabled="isSubmitting" > - + Завершено - + Отменено - + Активен - + Выполняется @@ -95,6 +95,7 @@ diff --git a/src/components/ui/AppStatus.vue b/src/components/ui/AppStatus.vue new file mode 100644 index 0000000..492e5a0 --- /dev/null +++ b/src/components/ui/AppStatus.vue @@ -0,0 +1,24 @@ + + + {{textMap[status]}} + + + + diff --git a/src/hooks/useRequestForm.ts b/src/hooks/useRequestForm.ts index 5194f72..0cf5b2e 100644 --- a/src/hooks/useRequestForm.ts +++ b/src/hooks/useRequestForm.ts @@ -1,3 +1,4 @@ +import { STATUS } from '@/utils/constants.ts'; import { toTypedSchema } from '@vee-validate/zod'; import { type SubmissionHandler, useField, useForm } from 'vee-validate'; import * as z from 'zod'; @@ -21,7 +22,7 @@ const schema = z.object({ .transform(value => Number(value || 0)), status: z - .enum(["done", "cancelled", "active", "pending"]) + .nativeEnum(STATUS) }); type Values = z.infer; @@ -35,7 +36,7 @@ const useRequestForm = (props: UseRequestFormProps) => { const {isSubmitting, handleSubmit} = useForm({ validationSchema: toTypedSchema(schema), initialValues: { - status: 'active', + status: STATUS.ACTIVE, amount: 0, phone: '', fio: '' diff --git a/src/router/index.ts b/src/router/index.ts index c01a98d..a69c1d1 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -4,11 +4,13 @@ import store from '../store'; export enum PAGE { HOME= 'Home', AUTH = 'Auth', - HELP = 'Help' + HELP = 'Help', + REQUEST = 'Request' } export const PAGE_URL: Record = { [PAGE.HOME]: '/', + [PAGE.REQUEST]: '/:id', [PAGE.AUTH]: '/auth', [PAGE.HELP]: '/help' } @@ -40,6 +42,14 @@ const routes = [ layout: 'auth', } }, + { + path: PAGE_URL[PAGE.REQUEST], + name: PAGE.REQUEST, + component: () => import('../views/AuthPage.vue'), + meta: { + layout: 'main', + } + }, ] const router = createRouter({ diff --git a/src/store/modules/request.module.ts b/src/store/modules/request.module.ts index 1832469..c67cc19 100644 --- a/src/store/modules/request.module.ts +++ b/src/store/modules/request.module.ts @@ -1,21 +1,22 @@ import { requestApi } from '@/api/request.ts'; import type { Module } from 'vuex'; import authModule from './auth.module.ts'; - -type Request = string; +import type {AppRequest} from '@/utils/constants' type State = { - requests: Request[]; + requests: AppRequest[]; }; type SetRequestsPayload = { - requests: State['requests'] + requests: AppRequest[] } type AddRequestsPayload = { - request: Request + request: AppRequest } +type CreateRequestPayload = Omit + const requestModule: Module = { namespaced: true, state: { @@ -33,13 +34,25 @@ const requestModule: Module = { } }, actions:{ - create: async (injectee, payload) => { + loadRequests: async (injectee) => { + const {commit, dispatch} = injectee; + try { + const token = authModule.state.token; + const { data } = await requestApi.get>>(`/requests.json?auth=${token}`); + const requests = Object.entries(data).map(([id, value]) => ({id, ...value})) + commit('setRequests', { requests }) + } catch (e) { + await dispatch('alert/setAlert', {message: 'Ошибка при попытке получить список заявок', type: 'danger', title: 'Ошибка', time: 5000}, {root: true}) + } + }, + create: async (injectee, payload: CreateRequestPayload) => { const { dispatch, commit } = injectee; const token = authModule.state.token; try { - const { data } = await requestApi.post(`/requests.json?auth=${token}`, payload) - commit('addRequest', {...payload, id: data.name}) + const { data } = await requestApi.post>(`/requests.json?auth=${token}`, payload) + const request = {...payload, id: data.name } + commit('addRequest', { request }) await dispatch('alert/setAlert', {message: 'Заявка успешно создана', type: 'primary', title: 'Успех', time: 5000}, {root: true}) } catch (e) { await dispatch('alert/setAlert', {message: 'Ошибка при попытке создать заявку', type: 'danger', title: 'Ошибка', time: 5000}, {root: true}) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 9460c8f..a8d19f0 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -2,3 +2,18 @@ export const ERROR_MESSAGES = { 'INVALID_LOGIN_CREDENTIALS': 'Неправильный логин или пароль', 'auth-required': 'Требуется авторизация', } + +export enum STATUS { + ACTIVE = 'active', + CANCELLED = 'cancelled', + DONE = 'done', + PENDING = 'pending', +} + +export type AppRequest = { + id: string; + amount: number; + fio: string; + phone: string; + status: STATUS; +}; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts new file mode 100644 index 0000000..e936d7f --- /dev/null +++ b/src/utils/helpers.ts @@ -0,0 +1,4 @@ +export const currencyFormatter = new Intl.NumberFormat('ru-RU', { + currency: 'RUB', + style: 'currency', +}) diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue index f4b551a..2839ac9 100644 --- a/src/views/HomePage.vue +++ b/src/views/HomePage.vue @@ -18,10 +18,12 @@ > - + + + + -import { requestApi } from '@/api/request.ts'; +import RequestFilter from '@/components/request/RequestFilter.vue'; import RequestForm from '@/components/request/RequestForm.vue'; import RequestTable from '@/components/request/RequestTable.vue'; import AppModal from '@/components/ui/AppModal.vue'; @@ -49,23 +51,20 @@ const store = useStore(); const closeModal = () => modal.value = false; const openModal = () => modal.value = true; -const loading = ref(true); +const loading = ref(false); +const filters = ref({}); onMounted(async () => { - try { - loading.value = true; - const token = store.state.auth.token; - const { data } = await requestApi.get(`/requests.json?auth=${token}`); - const requests = Object.entries(data).map(([id, value]) => ({id, ...value})) - store.commit('request/setRequests', { requests }) - } catch (e) { - await store.dispatch('alert/setAlert', {message: 'Ошибка при попытке получить список заявок', type: 'danger', title: 'Ошибка', time: 5000}) - } finally { - loading.value = false - } + loading.value = true; + await store.dispatch('request/loadRequests'); + loading.value = false }) -const requests = computed(() => store.state.request.requests); +const requests = computed(() => { + return store.state.request.requests + .filter(item => filters.value.status ? item.status === filters.value.status : true) + .filter(item => filters.value.name ? item.fio.includes(filters.value.name) : true); +});