0.1.0 to dev from front-base
This commit is contained in:
commit
a9c20be67a
@ -5,6 +5,7 @@ public class ChangeRecordDto
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public Guid? SpendingGroupId { get; set; }
|
||||
public string? SpendingGroupName { get; set; }
|
||||
public decimal Sum { get; set; }
|
||||
public DateTime ChangedAt { get; set; }
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
namespace Contracts.DTO;
|
||||
|
||||
public class UserLoginDTO
|
||||
public class UserLoginDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
@ -11,5 +11,6 @@ public static class ChangeRecordMapper
|
||||
Id = dto.Id,
|
||||
Sum = dto.Sum,
|
||||
ChangedAt = dto.ChangedAt.ToString("dd.MM.yyyy"),
|
||||
SpendingGroupName = dto.SpendingGroupName ?? string.Empty
|
||||
};
|
||||
}
|
@ -6,4 +6,5 @@ public class ChangeRecordSearch
|
||||
public Guid? SpendingGroupId { get; set; }
|
||||
public DateTime? From { get; set; }
|
||||
public DateTime? To { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
@ -4,4 +4,5 @@ public class SpendingGroupSearch
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
@ -6,6 +6,6 @@ namespace Contracts.Services;
|
||||
|
||||
public interface IAuthService
|
||||
{
|
||||
public Task<UserViewModel> Login(UserLoginDTO loginData);
|
||||
public Task<UserViewModel> Login(UserLoginDto loginData);
|
||||
public Task<UserViewModel> Register(UserDto user);
|
||||
}
|
@ -5,4 +5,5 @@ public class ChangeRecordViewModel
|
||||
public Guid Id { get; set; }
|
||||
public decimal Sum { get; set; }
|
||||
public string ChangedAt { get; set; } = null!;
|
||||
public string SpendingGroupName { get; set; } = string.Empty;
|
||||
}
|
@ -18,7 +18,7 @@ public class AuthController : ControllerBase
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<UserViewModel>> Login([FromBody] UserLoginDTO loginData)
|
||||
public async Task<ActionResult<UserViewModel>> Login([FromBody] UserLoginDto loginData)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -20,9 +20,10 @@ if (app.Environment.IsDevelopment())
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.MigrateDb();
|
||||
|
||||
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
@ -70,8 +70,12 @@ public class ChangeRecordRepo : IChangeRecordRepo
|
||||
{
|
||||
query = query.Where(x => x.ChangedAt >= search.From && x.ChangedAt <= search.To);
|
||||
}
|
||||
if (search.UserId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.UserId == search.UserId);
|
||||
}
|
||||
}
|
||||
return await query.Select(x => x.ToDto()).ToListAsync();
|
||||
return await query.Include(x => x.SpendingGroup).Select(x => x.ToDto()).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<ChangeRecordDto?> Update(ChangeRecordDto changeRecord)
|
||||
|
@ -49,7 +49,9 @@ public class SpendingGroupRepo : ISpendingGroupRepo
|
||||
.Include(x => x.ChangeRecords)
|
||||
.Include(x => x.SpendingPlans)
|
||||
.FirstOrDefaultAsync(x => x.Id == search.Id
|
||||
|| x.Name == search.Name);
|
||||
|| (!string.IsNullOrWhiteSpace(search.Name)
|
||||
&& x.Name == search.Name
|
||||
&& x.UserId == search.UserId));
|
||||
|
||||
return group?.ToDto();
|
||||
}
|
||||
@ -67,9 +69,10 @@ public class SpendingGroupRepo : ISpendingGroupRepo
|
||||
query = query.Where(x => x.Id == search.Id);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search.Name))
|
||||
if (!string.IsNullOrWhiteSpace(search.Name) && search.UserId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase));
|
||||
query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase)
|
||||
&& x.UserId == search.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,8 @@ public static class ChangeRecordMapper
|
||||
Sum = changeRecord.Sum,
|
||||
ChangedAt = changeRecord.ChangedAt,
|
||||
SpendingGroupId = changeRecord.SpendingGroupId,
|
||||
UserId = changeRecord.UserId
|
||||
UserId = changeRecord.UserId,
|
||||
SpendingGroupName = changeRecord.SpendingGroup?.Name
|
||||
};
|
||||
|
||||
public static ChangeRecord ToModel(this ChangeRecordDto changeRecord)
|
||||
|
@ -17,7 +17,7 @@ public class AuthService : IAuthService
|
||||
_userRepo = userRepo;
|
||||
}
|
||||
|
||||
public async Task<UserViewModel> Login(UserLoginDTO loginData)
|
||||
public async Task<UserViewModel> Login(UserLoginDto loginData)
|
||||
{
|
||||
if (loginData == null || string.IsNullOrWhiteSpace(loginData.Name)
|
||||
|| string.IsNullOrWhiteSpace(loginData.Password))
|
||||
|
2
front/.gitignore
vendored
2
front/.gitignore
vendored
@ -22,3 +22,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.env
|
46
front/components.d.ts
vendored
Normal file
46
front/components.d.ts
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
ACol: typeof import('ant-design-vue/es')['Col']
|
||||
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||
AFlex: typeof import('ant-design-vue/es')['Flex']
|
||||
AForm: typeof import('ant-design-vue/es')['Form']
|
||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
ALayout: typeof import('ant-design-vue/es')['Layout']
|
||||
ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
|
||||
ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
|
||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||
ATable: typeof import('ant-design-vue/es')['Table']
|
||||
ATypographyText: typeof import('ant-design-vue/es')['TypographyText']
|
||||
ChangeRecordManager: typeof import('./src/components/support/ChangeRecordManager.vue')['default']
|
||||
ChangeRecordMenu: typeof import('./src/components/support/ChangeRecordMenu.vue')['default']
|
||||
Groups: typeof import('./src/components/pages/Groups.vue')['default']
|
||||
Header: typeof import('./src/components/main/Header.vue')['default']
|
||||
Home: typeof import('./src/components/pages/Home.vue')['default']
|
||||
Login: typeof import('./src/components/pages/Login.vue')['default']
|
||||
Manager: typeof import('./src/components/support/Manager.vue')['default']
|
||||
PlanManager: typeof import('./src/components/support/PlanManager.vue')['default']
|
||||
Plans: typeof import('./src/components/pages/Plans.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SignUp: typeof import('./src/components/pages/SignUp.vue')['default']
|
||||
SpendingGroupManager: typeof import('./src/components/support/SpendingGroupManager.vue')['default']
|
||||
}
|
||||
}
|
1716
front/package-lock.json
generated
1716
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,14 +6,22 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"gen-api": "swagger-typescript-api -r -o ./src/core/api/ --modular -p "
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.12"
|
||||
"@vueuse/core": "^12.0.0",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"dayjs": "^1.11.13",
|
||||
"pinia": "^2.2.8",
|
||||
"vue": "^3.5.12",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"swagger-typescript-api": "^13.0.23",
|
||||
"typescript": "~5.6.2",
|
||||
"unplugin-vue-components": "^0.27.5",
|
||||
"vite": "^5.4.10",
|
||||
"vue-tsc": "^2.1.8"
|
||||
}
|
||||
|
@ -1,9 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import Header from './components/main/Header.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<a-layout class="layout">
|
||||
<Header />
|
||||
<a-layout-content>
|
||||
<RouterView />
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 5vh;
|
||||
}
|
||||
|
||||
.base-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 80dvw;
|
||||
}
|
||||
</style>
|
59
front/src/components/main/Header.vue
Normal file
59
front/src/components/main/Header.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { inject } from 'vue';
|
||||
import { useUserStore } from '../../store';
|
||||
import { AuthService } from '../../core/services/auth-service';
|
||||
import router from '../../router';
|
||||
|
||||
const store = useUserStore();
|
||||
const authService = inject(AuthService.name) as AuthService;
|
||||
|
||||
function logout() {
|
||||
authService.logout();
|
||||
router.push({ name: 'login' });
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-layout-header class="header">
|
||||
<div class="base-nav">
|
||||
<div>ДомБюдж</div>
|
||||
<nav>
|
||||
<RouterLink :to="{ name: 'home' }">Главная</RouterLink>
|
||||
<RouterLink :to="{ name: 'groups' }">Группы расходов</RouterLink>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-if="!store.user.id">
|
||||
<RouterLink :to="{ name: 'login' }">Войти</RouterLink>
|
||||
</div>
|
||||
<div v-else>
|
||||
<label for="logout">Привет, {{ store.user.name }}! Ваш текущий баланс: {{ store.user.balance }}</label>
|
||||
<a-button
|
||||
name="logout"
|
||||
@click="logout()"
|
||||
danger
|
||||
style="margin-left: 30px"
|
||||
>
|
||||
Выйти
|
||||
</a-button>
|
||||
</div>
|
||||
</a-layout-header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
background-color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.base-nav {
|
||||
display: inline-flex;
|
||||
justify-content: left;
|
||||
}
|
||||
.base-nav a {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
</style>
|
72
front/src/components/pages/Groups.vue
Normal file
72
front/src/components/pages/Groups.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import { useAsyncState } from '@vueuse/core';
|
||||
import { inject } from 'vue';
|
||||
import { GroupService } from '../../core/services/group-service';
|
||||
import SpendingGroupManager from '../support/SpendingGroupManager.vue';
|
||||
import { DeleteOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const groupService = inject(GroupService.name) as GroupService;
|
||||
|
||||
const { state, isReady } = useAsyncState(() => groupService.getList(), []);
|
||||
const columns = [
|
||||
{
|
||||
title: "Название группы",
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
},
|
||||
{
|
||||
title: "Планы группы",
|
||||
dataIndex: "plans",
|
||||
key: "plans",
|
||||
},
|
||||
{
|
||||
title: 'Операция',
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
}
|
||||
]
|
||||
|
||||
const refreshData = () => {
|
||||
groupService.getList().then(data => {
|
||||
state.value = data;
|
||||
isReady.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
const onDelete = (key: string) => {
|
||||
groupService.deleteGroup(key)
|
||||
.then(() => {
|
||||
refreshData();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="base-page">
|
||||
<h1>Группы расходов</h1>
|
||||
<SpendingGroupManager :refreshData="refreshData" />
|
||||
<a-table :dataSource="state" :columns="columns" v-if="isReady">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'plans'">
|
||||
<RouterLink :to="{ name: 'plans', params: { groupId: record.id } }" >Планы</RouterLink>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-popconfirm
|
||||
v-if="state?.length"
|
||||
title="Точно удалить?"
|
||||
@confirm="onDelete(record.id)"
|
||||
>
|
||||
<a><DeleteOutlined /> Удалить</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div v-else>
|
||||
<a-spin size="large" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
76
front/src/components/pages/Home.vue
Normal file
76
front/src/components/pages/Home.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<script setup lang="ts">
|
||||
import { useAsyncState } from '@vueuse/core';
|
||||
import ChangeRecordMenu from '../support/ChangeRecordManager.vue';
|
||||
import { ChangeRecordService } from '../../core/services/change-record-service';
|
||||
import { inject } from 'vue';
|
||||
import { DeleteOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const changeRecordService = inject(ChangeRecordService.name) as ChangeRecordService;
|
||||
|
||||
const { state, isReady } = useAsyncState(() => changeRecordService.getList(), []);
|
||||
const columns = [
|
||||
{
|
||||
title: 'Дата',
|
||||
dataIndex: 'changedAt',
|
||||
key: 'changedAt',
|
||||
},
|
||||
{
|
||||
title: 'Сумма',
|
||||
dataIndex: 'sum',
|
||||
key: 'sum',
|
||||
},
|
||||
{
|
||||
title: 'Группа расходов',
|
||||
dataIndex: 'spendingGroupName',
|
||||
key: 'spendingGroupName',
|
||||
},
|
||||
{
|
||||
title: 'Операция',
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
}
|
||||
]
|
||||
|
||||
const refreshData = () => {
|
||||
changeRecordService.getList().then(data => {
|
||||
state.value = data;
|
||||
isReady.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
const onDelete = (key: string) => {
|
||||
changeRecordService.deleteRecord(key)
|
||||
.then(() => {
|
||||
refreshData();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="base-page">
|
||||
<h1>История изменений баланса</h1>
|
||||
<ChangeRecordMenu :refreshData="refreshData" />
|
||||
<a-table :dataSource="state" :columns="columns" v-if="isReady" >
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-popconfirm
|
||||
v-if="state?.length"
|
||||
title="Точно удалить?"
|
||||
@confirm="onDelete(record.id)"
|
||||
>
|
||||
<a><DeleteOutlined /> Удалить</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div v-else>
|
||||
<a-spin size="large" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.layout {
|
||||
|
||||
}
|
||||
</style>
|
76
front/src/components/pages/Login.vue
Normal file
76
front/src/components/pages/Login.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="login"
|
||||
class="login-form"
|
||||
@finish="onFinish"
|
||||
@finishFailed="onFinishFailed"
|
||||
>
|
||||
<a-form-item
|
||||
label="Логин"
|
||||
name="name"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите свой логин' }]"
|
||||
>
|
||||
<a-input v-model:value="formState.name">
|
||||
<template #prefix>
|
||||
<UserOutlined class="site-form-item-icon" />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="Пароль"
|
||||
name="password"
|
||||
:rules="[{ required: true, message: 'Пароль тоже нужен!' }]"
|
||||
>
|
||||
<a-input-password v-model:value="formState.password">
|
||||
<template #prefix>
|
||||
<LockOutlined class="site-form-item-icon" />
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button :disabled="disabled" type="primary" html-type="submit" class="login-form-button">
|
||||
Войти
|
||||
</a-button>
|
||||
Или
|
||||
<RouterLink :to="{ name: 'signup' }">создать аккаунт</RouterLink>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed, inject } from 'vue';
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue';
|
||||
import { UserLoginDto } from '../../core/api/data-contracts';
|
||||
import { RouterLink } from 'vue-router';
|
||||
import { AuthService } from '../../core/services/auth-service';
|
||||
import router from '../../router';
|
||||
|
||||
const formState = reactive<UserLoginDto>({
|
||||
name: '',
|
||||
password: '',
|
||||
});
|
||||
const authService = inject(AuthService.name) as AuthService;
|
||||
console.log(authService);
|
||||
// Логика формы
|
||||
const onFinish = async (values: any) => {
|
||||
console.log('Success:', values);
|
||||
await authService.login(formState);
|
||||
router.push({ name: 'home' });
|
||||
};
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
const disabled = computed(() => {
|
||||
return !(formState.name && formState.password);
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.login-form {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.login-form-button {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
79
front/src/components/pages/Plans.vue
Normal file
79
front/src/components/pages/Plans.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import { useAsyncState } from '@vueuse/core';
|
||||
import { inject } from 'vue';
|
||||
import { PlanService } from '../../core/services/plans-service';
|
||||
import { useRoute } from 'vue-router';
|
||||
import PlanManager from '../support/PlanManager.vue';
|
||||
import { DeleteOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const planService = inject(PlanService.name) as PlanService;
|
||||
const groupId = useRoute().params.groupId as string;
|
||||
|
||||
|
||||
|
||||
const { state, isReady } = useAsyncState(() => planService.getList(groupId), []);
|
||||
const columns = [
|
||||
{
|
||||
title: "Планируемые расходы",
|
||||
dataIndex: "sum",
|
||||
key: "sum",
|
||||
},
|
||||
{
|
||||
title: "Начало плана",
|
||||
dataIndex: "startAt",
|
||||
key: "startAt",
|
||||
},
|
||||
{
|
||||
title: "Конец плана",
|
||||
dataIndex: "endAt",
|
||||
key: "endAt",
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Операция',
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
}
|
||||
]
|
||||
|
||||
const refreshData = () => {
|
||||
planService.getList(groupId).then(data => {
|
||||
state.value = data;
|
||||
isReady.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
const onDelete = (key: string) => {
|
||||
planService.deletePlan(key)
|
||||
.then(() => {
|
||||
refreshData();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="base-page">
|
||||
<h1>Планы группы</h1>
|
||||
<PlanManager :groupId="groupId" :refreshData="refreshData"/>
|
||||
<a-table :dataSource="state" :columns="columns" v-if="isReady">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-popconfirm
|
||||
v-if="state?.length"
|
||||
title="Точно удалить?"
|
||||
@confirm="onDelete(record.id)"
|
||||
>
|
||||
<a><DeleteOutlined /> Удалить</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div v-else>
|
||||
<a-spin size="large" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
86
front/src/components/pages/SignUp.vue
Normal file
86
front/src/components/pages/SignUp.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="signup"
|
||||
class="signup-form"
|
||||
@finish="onFinish"
|
||||
@finishFailed="onFinishFailed"
|
||||
>
|
||||
<a-form-item
|
||||
label="Логин"
|
||||
name="name"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите свой логин' }]"
|
||||
>
|
||||
<a-input v-model:value="formState.name">
|
||||
<template #prefix>
|
||||
<UserOutlined class="site-form-item-icon" />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="Пароль"
|
||||
name="password"
|
||||
:rules="[{ required: true, message: 'Пароль тоже нужен!' }]"
|
||||
>
|
||||
<a-input-password v-model:value="formState.password">
|
||||
<template #prefix>
|
||||
<LockOutlined class="site-form-item-icon" />
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="Баланс"
|
||||
name="balance"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите свой баланс' }]"
|
||||
>
|
||||
<a-input-number prefix="₽" v-model:value="formState.balance" min="0"/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button :disabled="disabled" type="primary" html-type="submit" class="signup-form-button">
|
||||
Создать
|
||||
</a-button>
|
||||
Или
|
||||
<RouterLink :to="{ name: 'login' }">войти в свой аккаунт</RouterLink>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, computed, inject } from 'vue';
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue';
|
||||
import { UserDto } from '../../core/api/data-contracts';
|
||||
import { RouterLink } from 'vue-router';
|
||||
import { AuthService } from '../../core/services/auth-service';
|
||||
import router from '../../router';
|
||||
|
||||
const formState = reactive<UserDto>({
|
||||
name: '',
|
||||
password: '',
|
||||
balance: 0
|
||||
});
|
||||
const authService = inject(typeof(AuthService)) as AuthService;
|
||||
|
||||
// Логика формы
|
||||
const onFinish = async (values: any) => {
|
||||
console.log('Success:', values);
|
||||
await authService.register(formState);
|
||||
router.push({ name: 'home' });
|
||||
};
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
const disabled = computed(() => {
|
||||
return !(formState.name && formState.password && formState.balance);
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.signup-form {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.signup-form-button {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
95
front/src/components/support/ChangeRecordManager.vue
Normal file
95
front/src/components/support/ChangeRecordManager.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<style scoped>
|
||||
</style>
|
||||
<template>
|
||||
<a-space direction="vertical" :size="10">
|
||||
<label for="change-record">Записать изменения баланса</label>
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="change-record"
|
||||
@finish="onFinish"
|
||||
@finishFailed="onFinishFailed"
|
||||
>
|
||||
<a-form-item
|
||||
label="Сумма"
|
||||
name="sum"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите сумму' }]"
|
||||
>
|
||||
<a-input-number v-model:value="formState.sum" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Группа расходов"
|
||||
name="spendingGroupId"
|
||||
v-if="isReady"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, выберите группу расходов' }]"
|
||||
>
|
||||
|
||||
<a-select v-model:value="formState.spendingGroupId" :disabled="disabled">
|
||||
<a-select-option v-for="group in groupList" :key="group.id" :value="group.id">
|
||||
{{ group.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-else>
|
||||
<a-spin size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
name="changedAt"
|
||||
label="Дата"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, выберите дату' }]"
|
||||
>
|
||||
<a-date-picker v-model:value="pickedChangedAt" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button html-type="submit" :disabled="disabledForm" type="primary">Сохранить</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-space>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, reactive, ref } from 'vue';
|
||||
import { ChangeRecordDto, SpendingGroupViewModel } from '../../core/api/data-contracts';
|
||||
import { useUserStore } from '../../store';
|
||||
import { ChangeRecordService } from '../../core/services/change-record-service';
|
||||
import { GroupService } from '../../core/services/group-service';
|
||||
import { useAsyncState } from '@vueuse/core';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
interface IProps {
|
||||
refreshData: () => void
|
||||
}
|
||||
|
||||
const { refreshData } = defineProps<IProps>();
|
||||
|
||||
const store = useUserStore();
|
||||
// Сервисы
|
||||
const changeRecordService = inject(ChangeRecordService.name) as ChangeRecordService;
|
||||
const groupService = inject(GroupService.name) as GroupService;
|
||||
|
||||
const { state: groupList, isReady } = useAsyncState(() => groupService.getList(), []);
|
||||
const pickedChangedAt = ref<Dayjs>();
|
||||
const formState = reactive<ChangeRecordDto>({
|
||||
userId: store.user.id,
|
||||
sum: -1,
|
||||
changedAt: new Date().toISOString(),
|
||||
spendingGroupId: null
|
||||
});
|
||||
|
||||
// Логика формы
|
||||
const onFinish = async (values: any) => {
|
||||
console.log('Success:', values);
|
||||
formState.changedAt = pickedChangedAt.value?.toISOString();
|
||||
await changeRecordService.createRecord(formState);
|
||||
refreshData();
|
||||
};
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
const disabled = computed(() => {
|
||||
return formState.sum && formState.sum >= 0;
|
||||
});
|
||||
const disabledForm = computed(() => {
|
||||
return !(formState.sum && formState.changedAt && (formState.spendingGroupId || formState.sum > 0));
|
||||
})
|
||||
</script>
|
80
front/src/components/support/PlanManager.vue
Normal file
80
front/src/components/support/PlanManager.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<style scoped>
|
||||
</style>
|
||||
<template>
|
||||
<a-space direction="vertical" :size="10">
|
||||
<label for="change-record">Добавить план расходов</label>
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="change-record"
|
||||
@finish="onFinish"
|
||||
@finishFailed="onFinishFailed"
|
||||
>
|
||||
<a-form-item
|
||||
label="Сумма"
|
||||
name="sum"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите сумму' }]"
|
||||
>
|
||||
<a-input-number v-model:value="formState.sum" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
name="startAt"
|
||||
label="Дата начала плана"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите дату начала плана' }]"
|
||||
>
|
||||
<a-date-picker v-model:value="startAt" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
name="endAt"
|
||||
label="Дата окончания плана"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите дату окончания плана' }]"
|
||||
>
|
||||
<a-date-picker v-model:value="endAt" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button :disabled="disabledForm" html-type="submit" type="primary">Добавить</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-space>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, reactive, ref } from 'vue';
|
||||
import { SpendingPlanDto } from '../../core/api/data-contracts';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import { PlanService } from '../../core/services/plans-service';
|
||||
|
||||
interface IProps {
|
||||
groupId: string,
|
||||
refreshData: () => void
|
||||
}
|
||||
const { groupId, refreshData } = defineProps<IProps>();
|
||||
// Сервисы
|
||||
const planService = inject(PlanService.name) as PlanService;
|
||||
|
||||
const startAt = ref<Dayjs>();
|
||||
const endAt = ref<Dayjs>();
|
||||
|
||||
const formState = reactive<SpendingPlanDto>({
|
||||
sum: 0,
|
||||
startAt: new Date().toISOString(),
|
||||
endAt: new Date().toISOString(),
|
||||
spendingGroupId: groupId
|
||||
});
|
||||
|
||||
// Логика формы
|
||||
const onFinish = async (values: any) => {
|
||||
console.log('Success:', values);
|
||||
formState.startAt = startAt.value?.toISOString();
|
||||
formState.endAt = endAt.value?.toISOString();
|
||||
await planService.createPlan(formState);
|
||||
refreshData();
|
||||
};
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
|
||||
const disabledForm = computed(() => {
|
||||
return !(formState.sum && formState.startAt && formState.endAt);
|
||||
});
|
||||
</script>
|
57
front/src/components/support/SpendingGroupManager.vue
Normal file
57
front/src/components/support/SpendingGroupManager.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<a-space direction="vertical" :size="10">
|
||||
<label for="spending-group">Создать группу расходов</label>
|
||||
<a-form
|
||||
:model="formState"
|
||||
name="spending-group"
|
||||
@finish="onFinish"
|
||||
@finishFailed="onFinishFailed"
|
||||
>
|
||||
<a-form-item
|
||||
label="Название"
|
||||
name="name"
|
||||
:rules="[{ required: true, message: 'Пожалуйста, введите название' }]"
|
||||
>
|
||||
<a-input v-model:value="formState.name" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button :disabled="disabledForm" html-type="submit" type="primary">Сохранить</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-space>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, reactive } from 'vue';
|
||||
import { useUserStore } from '../../store';
|
||||
import { GroupService } from '../../core/services/group-service';
|
||||
import { SpendingGroupDto } from '../../core/api/data-contracts';
|
||||
|
||||
interface IProps {
|
||||
refreshData: () => void
|
||||
}
|
||||
|
||||
const { refreshData } = defineProps<IProps>();
|
||||
|
||||
const store = useUserStore();
|
||||
// Сервисы
|
||||
const groupService = inject(GroupService.name) as GroupService;
|
||||
|
||||
const formState = reactive<SpendingGroupDto>({
|
||||
userId: store.user.id,
|
||||
name: '',
|
||||
});
|
||||
|
||||
// Логика формы
|
||||
const onFinish = async (values: any) => {
|
||||
console.log('Success:', values);
|
||||
await groupService.createGroup(formState);
|
||||
refreshData();
|
||||
};
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
|
||||
const disabledForm = computed(() => {
|
||||
return !formState.name;
|
||||
});
|
||||
</script>
|
470
front/src/core/api/Api.ts
Normal file
470
front/src/core/api/Api.ts
Normal file
@ -0,0 +1,470 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import {
|
||||
ChangeRecordDto,
|
||||
ChangeRecordViewModel,
|
||||
SpendingGroupDto,
|
||||
SpendingGroupViewModel,
|
||||
SpendingPlanDto,
|
||||
SpendingPlanViewModel,
|
||||
UserDto,
|
||||
UserLoginDto,
|
||||
UserViewModel,
|
||||
} from "./data-contracts";
|
||||
import { ContentType, HttpClient, RequestParams } from "./http-client";
|
||||
|
||||
export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Auth
|
||||
* @name AuthCreate
|
||||
* @request POST:/api/Auth
|
||||
* @response `200` `UserViewModel` Success
|
||||
*/
|
||||
auth = (data: UserLoginDto, params: RequestParams = {}) =>
|
||||
this.request<UserViewModel, any>({
|
||||
path: `/api/Auth`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Auth
|
||||
* @name AuthRegisterCreate
|
||||
* @request POST:/api/Auth/register
|
||||
* @response `200` `UserViewModel` Success
|
||||
*/
|
||||
authRegister = (data: UserDto, params: RequestParams = {}) =>
|
||||
this.request<UserViewModel, any>({
|
||||
path: `/api/Auth/register`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags ChangeRecord
|
||||
* @name ChangeRecordCreate
|
||||
* @request POST:/api/ChangeRecord
|
||||
* @response `200` `ChangeRecordViewModel` Success
|
||||
*/
|
||||
changeRecordCreate = (data: ChangeRecordDto, params: RequestParams = {}) =>
|
||||
this.request<ChangeRecordViewModel, any>({
|
||||
path: `/api/ChangeRecord`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags ChangeRecord
|
||||
* @name ChangeRecordList
|
||||
* @request GET:/api/ChangeRecord
|
||||
* @response `200` `(ChangeRecordViewModel)[]` Success
|
||||
*/
|
||||
changeRecordList = (params: RequestParams = {}) =>
|
||||
this.request<ChangeRecordViewModel[], any>({
|
||||
path: `/api/ChangeRecord`,
|
||||
method: "GET",
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags ChangeRecord
|
||||
* @name ChangeRecordPartialUpdate
|
||||
* @request PATCH:/api/ChangeRecord
|
||||
* @response `200` `ChangeRecordViewModel` Success
|
||||
*/
|
||||
changeRecordPartialUpdate = (data: ChangeRecordDto, params: RequestParams = {}) =>
|
||||
this.request<ChangeRecordViewModel, any>({
|
||||
path: `/api/ChangeRecord`,
|
||||
method: "PATCH",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags ChangeRecord
|
||||
* @name ChangeRecordDelete
|
||||
* @request DELETE:/api/ChangeRecord
|
||||
* @response `200` `void` Success
|
||||
*/
|
||||
changeRecordDelete = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
/** @format uuid */
|
||||
SpendingGroupId?: string;
|
||||
/** @format date-time */
|
||||
From?: string;
|
||||
/** @format date-time */
|
||||
To?: string;
|
||||
/** @format uuid */
|
||||
UserId?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, any>({
|
||||
path: `/api/ChangeRecord`,
|
||||
method: "DELETE",
|
||||
query: query,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags ChangeRecord
|
||||
* @name ChangeRecordFilterList
|
||||
* @request GET:/api/ChangeRecord/filter
|
||||
* @response `200` `(ChangeRecordViewModel)[]` Success
|
||||
*/
|
||||
changeRecordFilterList = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
/** @format uuid */
|
||||
SpendingGroupId?: string;
|
||||
/** @format date-time */
|
||||
From?: string;
|
||||
/** @format date-time */
|
||||
To?: string;
|
||||
/** @format uuid */
|
||||
UserId?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<ChangeRecordViewModel[], any>({
|
||||
path: `/api/ChangeRecord/filter`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingGroup
|
||||
* @name SpendingGroupDetail
|
||||
* @request GET:/api/SpendingGroup/{id}
|
||||
* @response `200` `SpendingGroupViewModel` Success
|
||||
*/
|
||||
spendingGroupDetail = (
|
||||
id: string,
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
Name?: string;
|
||||
/** @format uuid */
|
||||
UserId?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<SpendingGroupViewModel, any>({
|
||||
path: `/api/SpendingGroup/${id}`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingGroup
|
||||
* @name SpendingGroupList
|
||||
* @request GET:/api/SpendingGroup
|
||||
* @response `200` `(SpendingGroupViewModel)[]` Success
|
||||
*/
|
||||
spendingGroupList = (params: RequestParams = {}) =>
|
||||
this.request<SpendingGroupViewModel[], any>({
|
||||
path: `/api/SpendingGroup`,
|
||||
method: "GET",
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingGroup
|
||||
* @name SpendingGroupCreate
|
||||
* @request POST:/api/SpendingGroup
|
||||
* @response `200` `SpendingGroupViewModel` Success
|
||||
*/
|
||||
spendingGroupCreate = (data: SpendingGroupDto, params: RequestParams = {}) =>
|
||||
this.request<SpendingGroupViewModel, any>({
|
||||
path: `/api/SpendingGroup`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingGroup
|
||||
* @name SpendingGroupPartialUpdate
|
||||
* @request PATCH:/api/SpendingGroup
|
||||
* @response `200` `SpendingGroupViewModel` Success
|
||||
*/
|
||||
spendingGroupPartialUpdate = (data: SpendingGroupDto, params: RequestParams = {}) =>
|
||||
this.request<SpendingGroupViewModel, any>({
|
||||
path: `/api/SpendingGroup`,
|
||||
method: "PATCH",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingGroup
|
||||
* @name SpendingGroupDelete
|
||||
* @request DELETE:/api/SpendingGroup
|
||||
* @response `200` `void` Success
|
||||
*/
|
||||
spendingGroupDelete = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
Name?: string;
|
||||
/** @format uuid */
|
||||
UserId?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, any>({
|
||||
path: `/api/SpendingGroup`,
|
||||
method: "DELETE",
|
||||
query: query,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingGroup
|
||||
* @name SpendingGroupFilterList
|
||||
* @request GET:/api/SpendingGroup/filter
|
||||
* @response `200` `(SpendingGroupViewModel)[]` Success
|
||||
*/
|
||||
spendingGroupFilterList = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
Name?: string;
|
||||
/** @format uuid */
|
||||
UserId?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<SpendingGroupViewModel[], any>({
|
||||
path: `/api/SpendingGroup/filter`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingPlan
|
||||
* @name SpendingPlanDetail
|
||||
* @request GET:/api/SpendingPlan/{id}
|
||||
* @response `200` `SpendingPlanViewModel` Success
|
||||
*/
|
||||
spendingPlanDetail = (
|
||||
id: string,
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<SpendingPlanViewModel, any>({
|
||||
path: `/api/SpendingPlan/${id}`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingPlan
|
||||
* @name SpendingPlanList
|
||||
* @request GET:/api/SpendingPlan
|
||||
* @response `200` `(SpendingPlanViewModel)[]` Success
|
||||
*/
|
||||
spendingPlanList = (params: RequestParams = {}) =>
|
||||
this.request<SpendingPlanViewModel[], any>({
|
||||
path: `/api/SpendingPlan`,
|
||||
method: "GET",
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingPlan
|
||||
* @name SpendingPlanCreate
|
||||
* @request POST:/api/SpendingPlan
|
||||
* @response `200` `SpendingPlanViewModel` Success
|
||||
*/
|
||||
spendingPlanCreate = (data: SpendingPlanDto, params: RequestParams = {}) =>
|
||||
this.request<SpendingPlanViewModel, any>({
|
||||
path: `/api/SpendingPlan`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingPlan
|
||||
* @name SpendingPlanPartialUpdate
|
||||
* @request PATCH:/api/SpendingPlan
|
||||
* @response `200` `SpendingPlanViewModel` Success
|
||||
*/
|
||||
spendingPlanPartialUpdate = (data: SpendingPlanDto, params: RequestParams = {}) =>
|
||||
this.request<SpendingPlanViewModel, any>({
|
||||
path: `/api/SpendingPlan`,
|
||||
method: "PATCH",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingPlan
|
||||
* @name SpendingPlanDelete
|
||||
* @request DELETE:/api/SpendingPlan
|
||||
* @response `200` `void` Success
|
||||
*/
|
||||
spendingPlanDelete = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, any>({
|
||||
path: `/api/SpendingPlan`,
|
||||
method: "DELETE",
|
||||
query: query,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags SpendingPlan
|
||||
* @name SpendingPlanFilterList
|
||||
* @request GET:/api/SpendingPlan/filter
|
||||
* @response `200` `(SpendingPlanViewModel)[]` Success
|
||||
*/
|
||||
spendingPlanFilterList = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<SpendingPlanViewModel[], any>({
|
||||
path: `/api/SpendingPlan/filter`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags User
|
||||
* @name UserList
|
||||
* @request GET:/api/User
|
||||
* @response `200` `UserViewModel` Success
|
||||
*/
|
||||
userGet = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
Name?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<UserViewModel, any>({
|
||||
path: `/api/User`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags User
|
||||
* @name UserPartialUpdate
|
||||
* @request PATCH:/api/User
|
||||
* @response `200` `UserViewModel` Success
|
||||
*/
|
||||
userPartialUpdate = (data: UserDto, params: RequestParams = {}) =>
|
||||
this.request<UserViewModel, any>({
|
||||
path: `/api/User`,
|
||||
method: "PATCH",
|
||||
body: data,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags User
|
||||
* @name UserDelete
|
||||
* @request DELETE:/api/User
|
||||
* @response `200` `UserViewModel` Success
|
||||
*/
|
||||
userDelete = (
|
||||
query?: {
|
||||
/** @format uuid */
|
||||
Id?: string;
|
||||
Name?: string;
|
||||
},
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<UserViewModel, any>({
|
||||
path: `/api/User`,
|
||||
method: "DELETE",
|
||||
query: query,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
}
|
98
front/src/core/api/data-contracts.ts
Normal file
98
front/src/core/api/data-contracts.ts
Normal file
@ -0,0 +1,98 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export interface ChangeRecordDto {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
/** @format uuid */
|
||||
userId?: string;
|
||||
/** @format uuid */
|
||||
spendingGroupId?: string | null;
|
||||
spendingGroupName?: string | null;
|
||||
/** @format double */
|
||||
sum?: number;
|
||||
/** @format date-time */
|
||||
changedAt?: string;
|
||||
}
|
||||
|
||||
export interface ChangeRecordViewModel {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
/** @format double */
|
||||
sum?: number;
|
||||
/** @format date-time */
|
||||
changedAt?: string;
|
||||
spendingGroupName?: string | null;
|
||||
}
|
||||
|
||||
export interface SpendingGroupDto {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
name?: string | null;
|
||||
/** @format uuid */
|
||||
userId?: string;
|
||||
changeRecords?: ChangeRecordDto[] | null;
|
||||
spendingPlans?: SpendingPlanDto[] | null;
|
||||
}
|
||||
|
||||
export interface SpendingGroupViewModel {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
name?: string | null;
|
||||
changeRecords?: ChangeRecordViewModel[] | null;
|
||||
spendingPlans?: SpendingPlanViewModel[] | null;
|
||||
}
|
||||
|
||||
export interface SpendingPlanDto {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
/** @format uuid */
|
||||
spendingGroupId?: string;
|
||||
/** @format double */
|
||||
sum?: number;
|
||||
/** @format date-time */
|
||||
startAt?: string;
|
||||
/** @format date-time */
|
||||
endAt?: string;
|
||||
}
|
||||
|
||||
export interface SpendingPlanViewModel {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
/** @format date-time */
|
||||
startAt?: string;
|
||||
/** @format date-time */
|
||||
endAt?: string;
|
||||
/** @format double */
|
||||
sum?: number;
|
||||
}
|
||||
|
||||
export interface UserDto {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
name?: string | null;
|
||||
password?: string | null;
|
||||
/** @format double */
|
||||
balance?: number;
|
||||
}
|
||||
|
||||
export interface UserLoginDto {
|
||||
name?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
|
||||
export interface UserViewModel {
|
||||
/** @format uuid */
|
||||
id?: string;
|
||||
name?: string | null;
|
||||
/** @format double */
|
||||
balance?: number;
|
||||
}
|
220
front/src/core/api/http-client.ts
Normal file
220
front/src/core/api/http-client.ts
Normal file
@ -0,0 +1,220 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export type QueryParamsType = Record<string | number, any>;
|
||||
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
|
||||
|
||||
export interface FullRequestParams extends Omit<RequestInit, "body"> {
|
||||
/** set parameter to `true` for call `securityWorker` for this request */
|
||||
secure?: boolean;
|
||||
/** request path */
|
||||
path: string;
|
||||
/** content type of request body */
|
||||
type?: ContentType;
|
||||
/** query params */
|
||||
query?: QueryParamsType;
|
||||
/** format of response (i.e. response.json() -> format: "json") */
|
||||
format?: ResponseFormat;
|
||||
/** request body */
|
||||
body?: unknown;
|
||||
/** base url */
|
||||
baseUrl?: string;
|
||||
/** request cancellation token */
|
||||
cancelToken?: CancelToken;
|
||||
}
|
||||
|
||||
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
|
||||
|
||||
export interface ApiConfig<SecurityDataType = unknown> {
|
||||
baseUrl?: string;
|
||||
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
|
||||
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
|
||||
customFetch?: typeof fetch;
|
||||
}
|
||||
|
||||
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
|
||||
data: D;
|
||||
error: E;
|
||||
}
|
||||
|
||||
type CancelToken = Symbol | string | number;
|
||||
|
||||
export enum ContentType {
|
||||
Json = "application/json",
|
||||
FormData = "multipart/form-data",
|
||||
UrlEncoded = "application/x-www-form-urlencoded",
|
||||
Text = "text/plain",
|
||||
}
|
||||
|
||||
export class HttpClient<SecurityDataType = unknown> {
|
||||
public baseUrl: string = "http://localhost:5125";
|
||||
private securityData: SecurityDataType | null = null;
|
||||
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
|
||||
private abortControllers = new Map<CancelToken, AbortController>();
|
||||
private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
|
||||
|
||||
private baseApiParams: RequestParams = {
|
||||
credentials: "same-origin",
|
||||
headers: {},
|
||||
redirect: "follow",
|
||||
referrerPolicy: "no-referrer",
|
||||
};
|
||||
|
||||
constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
|
||||
Object.assign(this, apiConfig);
|
||||
}
|
||||
|
||||
public setSecurityData = (data: SecurityDataType | null) => {
|
||||
this.securityData = data;
|
||||
};
|
||||
|
||||
protected encodeQueryParam(key: string, value: any) {
|
||||
const encodedKey = encodeURIComponent(key);
|
||||
return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
|
||||
}
|
||||
|
||||
protected addQueryParam(query: QueryParamsType, key: string) {
|
||||
return this.encodeQueryParam(key, query[key]);
|
||||
}
|
||||
|
||||
protected addArrayQueryParam(query: QueryParamsType, key: string) {
|
||||
const value = query[key];
|
||||
return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
|
||||
}
|
||||
|
||||
protected toQueryString(rawQuery?: QueryParamsType): string {
|
||||
const query = rawQuery || {};
|
||||
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
|
||||
return keys
|
||||
.map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
|
||||
.join("&");
|
||||
}
|
||||
|
||||
protected addQueryParams(rawQuery?: QueryParamsType): string {
|
||||
const queryString = this.toQueryString(rawQuery);
|
||||
return queryString ? `?${queryString}` : "";
|
||||
}
|
||||
|
||||
private contentFormatters: Record<ContentType, (input: any) => any> = {
|
||||
[ContentType.Json]: (input: any) =>
|
||||
input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
|
||||
[ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input),
|
||||
[ContentType.FormData]: (input: any) =>
|
||||
Object.keys(input || {}).reduce((formData, key) => {
|
||||
const property = input[key];
|
||||
formData.append(
|
||||
key,
|
||||
property instanceof Blob
|
||||
? property
|
||||
: typeof property === "object" && property !== null
|
||||
? JSON.stringify(property)
|
||||
: `${property}`,
|
||||
);
|
||||
return formData;
|
||||
}, new FormData()),
|
||||
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
|
||||
};
|
||||
|
||||
protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
|
||||
return {
|
||||
...this.baseApiParams,
|
||||
...params1,
|
||||
...(params2 || {}),
|
||||
headers: {
|
||||
...(this.baseApiParams.headers || {}),
|
||||
...(params1.headers || {}),
|
||||
...((params2 && params2.headers) || {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
|
||||
if (this.abortControllers.has(cancelToken)) {
|
||||
const abortController = this.abortControllers.get(cancelToken);
|
||||
if (abortController) {
|
||||
return abortController.signal;
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
this.abortControllers.set(cancelToken, abortController);
|
||||
return abortController.signal;
|
||||
};
|
||||
|
||||
public abortRequest = (cancelToken: CancelToken) => {
|
||||
const abortController = this.abortControllers.get(cancelToken);
|
||||
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
this.abortControllers.delete(cancelToken);
|
||||
}
|
||||
};
|
||||
|
||||
public request = async <T = any, E = any>({
|
||||
body,
|
||||
secure,
|
||||
path,
|
||||
type,
|
||||
query,
|
||||
format,
|
||||
baseUrl,
|
||||
cancelToken,
|
||||
...params
|
||||
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
|
||||
const secureParams =
|
||||
((typeof secure === "boolean" ? secure : this.baseApiParams.secure) &&
|
||||
this.securityWorker &&
|
||||
(await this.securityWorker(this.securityData))) ||
|
||||
{};
|
||||
const requestParams = this.mergeRequestParams(params, secureParams);
|
||||
const queryString = query && this.toQueryString(query);
|
||||
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
|
||||
const responseFormat = format || requestParams.format;
|
||||
|
||||
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
|
||||
...requestParams,
|
||||
headers: {
|
||||
...(requestParams.headers || {}),
|
||||
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
|
||||
},
|
||||
signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
|
||||
body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
|
||||
}).then(async (response) => {
|
||||
const r = response.clone() as HttpResponse<T, E>;
|
||||
r.data = null as unknown as T;
|
||||
r.error = null as unknown as E;
|
||||
|
||||
const data = !responseFormat
|
||||
? r
|
||||
: await response[responseFormat]()
|
||||
.then((data) => {
|
||||
if (r.ok) {
|
||||
r.data = data;
|
||||
} else {
|
||||
r.error = data;
|
||||
}
|
||||
return r;
|
||||
})
|
||||
.catch((e) => {
|
||||
r.error = e;
|
||||
return r;
|
||||
});
|
||||
|
||||
if (cancelToken) {
|
||||
this.abortControllers.delete(cancelToken);
|
||||
}
|
||||
|
||||
if (!response.ok) throw data;
|
||||
return data;
|
||||
});
|
||||
};
|
||||
}
|
30
front/src/core/services/auth-service.ts
Normal file
30
front/src/core/services/auth-service.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { useUserStore } from "../../store";
|
||||
import { Api } from "../api/Api";
|
||||
import { UserDto, UserLoginDto, UserViewModel } from "../api/data-contracts";
|
||||
|
||||
export class AuthService {
|
||||
private readonly _api: Api;
|
||||
|
||||
constructor(api: Api) {
|
||||
this._api = api;
|
||||
|
||||
}
|
||||
async login(data: UserLoginDto) {
|
||||
let result = await this._api.auth(data);
|
||||
console.log(result);
|
||||
const store = useUserStore();
|
||||
store.updateUser(result.data);
|
||||
}
|
||||
|
||||
logout() {
|
||||
const store = useUserStore();
|
||||
store.updateUser({} as UserViewModel);
|
||||
}
|
||||
|
||||
async register(data: UserDto) {
|
||||
let result = await this._api.authRegister(data);
|
||||
const store = useUserStore();
|
||||
store.updateUser(result.data);
|
||||
}
|
||||
|
||||
}
|
42
front/src/core/services/change-record-service.ts
Normal file
42
front/src/core/services/change-record-service.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { useUserStore } from "../../store";
|
||||
import { Api } from "../api/Api";
|
||||
import { ChangeRecordDto, ChangeRecordViewModel } from "../api/data-contracts";
|
||||
|
||||
export class ChangeRecordService {
|
||||
private readonly _api: Api
|
||||
constructor(api: Api) {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
private async _updateUser() {
|
||||
const store = useUserStore();
|
||||
let updatedUser = await this._api.userGet({ Id: store.user.id });
|
||||
|
||||
store.updateUser(updatedUser.data);
|
||||
}
|
||||
|
||||
async createRecord(data: ChangeRecordDto) {
|
||||
let result = await this._api.changeRecordCreate(data);
|
||||
this._updateUser();
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
async deleteRecord(Id: string) {
|
||||
console.log(Id);
|
||||
let result = await this._api.changeRecordDelete({ Id });
|
||||
this._updateUser();
|
||||
console.log("delete");
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
async getList(): Promise<ChangeRecordViewModel[] | null> {
|
||||
const store = useUserStore();
|
||||
if (!store.user.id) return null;
|
||||
|
||||
let UserId = store.user.id;
|
||||
let result = await this._api.changeRecordFilterList({ UserId });
|
||||
|
||||
console.log(result);
|
||||
return result.data;
|
||||
}
|
||||
}
|
33
front/src/core/services/group-service.ts
Normal file
33
front/src/core/services/group-service.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { useUserStore } from "../../store";
|
||||
import { Api } from "../api/Api";
|
||||
import { SpendingGroupDto, SpendingGroupViewModel } from "../api/data-contracts";
|
||||
|
||||
export class GroupService {
|
||||
private readonly _api: Api;
|
||||
constructor(api: Api) {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
async getList(): Promise<SpendingGroupViewModel[] | null> {
|
||||
const store = useUserStore();
|
||||
console.log("get list " + store.user.id);
|
||||
if (!store.user.id) return null;
|
||||
|
||||
let UserId = store.user.id;
|
||||
let result = await this._api.spendingGroupFilterList({ UserId });
|
||||
|
||||
console.log(result);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
async deleteGroup(Id: string) {
|
||||
let result = await this._api.spendingGroupDelete({ Id });
|
||||
console.log("delete");
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
async createGroup(data: SpendingGroupDto) {
|
||||
let result = await this._api.spendingGroupCreate(data);
|
||||
console.log(result);
|
||||
}
|
||||
}
|
26
front/src/core/services/plans-service.ts
Normal file
26
front/src/core/services/plans-service.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Api } from "../api/Api";
|
||||
import { SpendingPlanDto, SpendingPlanViewModel } from "../api/data-contracts";
|
||||
|
||||
export class PlanService {
|
||||
|
||||
private readonly _api: Api
|
||||
constructor(api: Api) {
|
||||
this._api = api;
|
||||
}
|
||||
|
||||
async deletePlan(Id: string) {
|
||||
let result = await this._api.spendingPlanDelete({ Id });
|
||||
console.log("delete");
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
async getList(groupId: string): Promise<SpendingPlanViewModel[] | null> {
|
||||
const result = await this._api.spendingGroupDetail(groupId);
|
||||
return result.data.spendingPlans || null;
|
||||
}
|
||||
|
||||
async createPlan(data: SpendingPlanDto) {
|
||||
const result = await this._api.spendingPlanCreate(data);
|
||||
console.log(result);
|
||||
}
|
||||
}
|
@ -1,5 +1,24 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { Api } from './core/api/Api'
|
||||
import { createPinia } from 'pinia'
|
||||
import { AuthService } from './core/services/auth-service'
|
||||
import { ChangeRecordService } from './core/services/change-record-service'
|
||||
import { GroupService } from './core/services/group-service'
|
||||
import { PlanService } from './core/services/plans-service'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
app.use(createPinia())
|
||||
|
||||
// Di
|
||||
const api = new Api();
|
||||
app.provide(AuthService.name, new AuthService(api));
|
||||
app.provide(ChangeRecordService.name, new ChangeRecordService(api));
|
||||
app.provide(GroupService.name, new GroupService(api));
|
||||
app.provide(PlanService.name, new PlanService(api));
|
||||
|
||||
app.mount('#app')
|
||||
|
46
front/src/router.ts
Normal file
46
front/src/router.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { useUserStore } from './store';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('./components/pages/Home.vue'),
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: () => import('./components/pages/Login.vue'),
|
||||
meta: { notRequiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
name: 'signup',
|
||||
component: () => import('./components/pages/SignUp.vue'),
|
||||
meta: { notRequiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/groups',
|
||||
name: 'groups',
|
||||
component: () => import('./components/pages/Groups.vue'),
|
||||
},
|
||||
{
|
||||
path: '/plans/:groupId',
|
||||
name: 'plans',
|
||||
component: () => import('./components/pages/Plans.vue'),
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const store = useUserStore();
|
||||
if (!to.meta.notRequiresAuth && !store.user.id) {
|
||||
next({ name: 'login' });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
19
front/src/store.ts
Normal file
19
front/src/store.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { UserViewModel } from './core/api/data-contracts'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export interface State {
|
||||
user: UserViewModel
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: (): State => ({
|
||||
user: {} as UserViewModel
|
||||
}),
|
||||
actions: {
|
||||
updateUser(payload: UserViewModel) {
|
||||
if (!payload) return;
|
||||
|
||||
this.user = payload;
|
||||
},
|
||||
}
|
||||
})
|
@ -1,7 +1,18 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
plugins: [
|
||||
vue(),
|
||||
Components({
|
||||
resolvers: [
|
||||
AntDesignVueResolver({
|
||||
importStyle: false, // css in js
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user