Skip to content

Commit c22f854

Browse files
improve: router + admin layout (not yet protected)
1 parent ac894b1 commit c22f854

File tree

23 files changed

+286
-128
lines changed

23 files changed

+286
-128
lines changed
963 KB
Loading

src/App.vue

+2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { theme } from '@/theme'
66
import AuthLayout from '@/layouts/auth/index.vue'
77
import DefaultLayout from '@/layouts/empty/index.vue'
88
import LandingLayout from '@/layouts/landing-page/index.vue'
9+
import AdminLayout from '@/layouts/admin/index.vue'
910
import { markRaw, ref, watch } from 'vue'
1011
import Curtain from '@/components/Curtain.vue'
1112
import AsyncErrorBoundary from '@/components/AsyncErrorBoundary.vue'
1213
1314
const layouts: Record<string, typeof DefaultLayout> = {
1415
default: DefaultLayout,
1516
auth: AuthLayout,
17+
admin: AdminLayout,
1618
'landing-page': LandingLayout
1719
}
1820
File renamed without changes.

src/api/auth/auth.dto.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ export type RefreshTokenResponse = {
2323

2424
export type RegisterRequest = {
2525
email: string
26-
firstName: string
27-
lastName: string
28-
address: string
29-
phone?: string
30-
country?: string
26+
name: string
3127
password: string
3228
}
3329

src/i18n/locales/en.ts

+27-3
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,44 @@ const localesEN = {
22
common: {
33
example: `When I find myself in times of trouble, Mother Mary comes to me. Speaking words of wisdom, "Let It Be". And in my hour of darkness she is standing right in front of me, speaking words of wisdom, "Let It Be".`,
44
feature_coming_soon: 'This feature is in development',
5-
oops: 'Oops!!!'
5+
oops: 'Oops!!!',
6+
this_field_is_required: 'Please fill out this field!'
67
},
78
login: {
89
big_title: 'Login',
910
form: {
1011
email: 'Email',
1112
password: 'Password',
12-
submit_btn: 'Login'
13+
submit_btn: 'Login',
14+
validation: {
15+
email: {
16+
required: 'Please enter your email'
17+
},
18+
password: {
19+
required: 'Please enter your password'
20+
}
21+
}
1322
},
1423
forgot_password: 'Forgot password?',
1524
register_reminder: "Don't have an account?",
1625
to_register: 'Register now'
1726
},
18-
register: {}
27+
register: {
28+
big_title: 'Register',
29+
form: {
30+
name: 'Full Name',
31+
email: 'Email',
32+
password: 'Password',
33+
confirm_password: 'Confirm Password',
34+
submit_btn: 'Register'
35+
},
36+
alert: {
37+
register_success: 'Registration successful',
38+
click_ok_to_direct_to_login_page: 'Click OK to go to the login page'
39+
},
40+
login_reminder: 'Already have an account?',
41+
to_login_page: 'Go to login page'
42+
}
1943
}
2044

2145
export default localesEN

src/i18n/locales/ja.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,44 @@ const localesJA = {
33
'自分は首を前へ出して冷たい露の滴る、白い花弁に接吻した。自分が百合から顔を離す拍子に思わず、遠い空を見たら、暁の星がたった一つ瞬いていた。「百年はもう来ていたんだな」とこの時始めて気がついた。',
44
feature_coming_soon: 'この機能は開発中です',
55
oops: 'おっと!!!',
6+
this_field_is_required: 'このフィールドを入力してください!',
67

78
common: {},
89
login: {
910
big_title: 'ログイン',
1011
form: {
1112
email: 'メールアドレス',
1213
password: 'パスワード',
13-
submit_btn: 'ログイン'
14+
submit_btn: 'ログイン',
15+
validation: {
16+
email: {
17+
required: 'メールアドレスを入力してください'
18+
},
19+
password: {
20+
required: 'パスワードを入力してください'
21+
}
22+
}
1423
},
1524
forgot_password: 'パスワードを忘れた場合',
1625
register_reminder: 'アカウントを持っていない場合',
1726
to_register: '今すぐ登録'
1827
},
19-
register: {}
28+
register: {
29+
big_title: '登録',
30+
form: {
31+
name: 'フルネーム',
32+
email: 'メールアドレス',
33+
password: 'パスワード',
34+
confirm_password: 'パスワードの確認',
35+
submit_btn: '登録する'
36+
},
37+
alert: {
38+
register_success: '登録成功',
39+
click_ok_to_direct_to_login_page: 'OKをクリックしてログインページに移動します'
40+
},
41+
login_reminder: 'すでにアカウントをお持ちですか?',
42+
to_login_page: 'ログインページへ'
43+
}
2044
}
2145

2246
export default localesJA

src/i18n/locales/vi.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ const localesVI = {
2828
register: {
2929
big_title: 'Đăng ký',
3030
form: {
31-
firstName: 'Họ',
32-
lastName: 'Tên',
31+
name: 'Tên đầy đủ',
3332
email: 'Địa chỉ email',
3433
password: 'Mật khẩu',
3534
confirm_password: 'Xác nhận mật khẩu',

src/layouts/admin/AdminSidebar.vue

+36-32
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
<script lang="ts" setup>
22
import { ref } from 'vue'
33
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'
4-
import { Modal } from 'ant-design-vue'
4+
import { Button, Menu, MenuItem, Modal, SubMenu } from 'ant-design-vue'
55
import { useAuthStore } from '@/stores/auth'
66
import { Icon } from '@iconify/vue'
7-
import router from '@/router'
7+
import router, { RoutePath } from '@/router'
8+
import type { SelectInfo } from 'ant-design-vue/es/menu/src/interface'
89
const auth = useAuthStore()
9-
// selected if current route name is equal to key or parent route name is equal to key
10+
11+
// selected if current route name is equal to key or parent route path is equal to key
1012
const selectedKeys = ref<string[]>([
1113
router.currentRoute.value.name as string,
12-
...router.currentRoute.value.matched.map((route) => route.name as string)
14+
...router.currentRoute.value.matched.map((route) => route.path as string)
1315
])
1416
const collapsed = ref<boolean>(false)
1517
const handleCollapse = () => {
1618
collapsed.value = !collapsed.value
1719
}
18-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
19-
const handleSelect = (event: any) => {
20-
router.push({ name: event?.key })
20+
21+
const handleSelect = (event: SelectInfo) => {
22+
if (!event.key) return
23+
24+
router.push(String(event.key))
2125
}
2226
2327
const handleLogout = () => {
@@ -34,13 +38,14 @@ const handleLogout = () => {
3438
}
3539
</script>
3640
<template>
37-
<div class="gap-4 bg-[#001529] flex flex-col text-white z-[666]">
38-
<a-button type="primary" block class="mb-8" @click="handleCollapse">
41+
<!-- ! MENU ITEM KEY MUST BE PATH -->
42+
<div class="gap-4 flex flex-col bg-white z-[666] border-r border-gray-400">
43+
<Button type="primary" block class="mb-8 rounded-none" @click="handleCollapse">
3944
<template #icon>
4045
<MenuUnfoldOutlined v-if="collapsed" />
4146
<MenuFoldOutlined v-else />
4247
</template>
43-
</a-button>
48+
</Button>
4449
<div
4550
class="flex flex-col gap-3 mb-12"
4651
:class="{ 'pl-6': !collapsed, 'items-center': collapsed }"
@@ -50,63 +55,62 @@ const handleLogout = () => {
5055
:class="{ 'w-24': !collapsed, 'w-12': collapsed }"
5156
>
5257
<img
53-
src="https://res.cloudinary.com/cyantiz/image/upload/v1670993022/YagamiLightRingo_moramg.png"
58+
src="/images/admin-avatar-example.jpg"
5459
class="w-full h-full object-cover"
5560
alt="#"
5661
/>
5762
</div>
5863
<div v-if="!collapsed">
5964
<div class="font-bold text-lg">Admin</div>
60-
<div class="text-gray-300">admin@pomme.tech</div>
65+
<div class="text-gray-500">admin@example.tech</div>
6166
</div>
6267
</div>
63-
<a-menu
68+
<Menu
6469
:style="{ width: collapsed ? '64px' : '256px' }"
6570
id="admin-side-menu"
6671
@select="handleSelect"
6772
v-model:selectedKeys="selectedKeys"
73+
theme="light"
6874
mode="inline"
69-
theme="dark"
7075
:inline-collapsed="collapsed"
71-
:open-keys="!collapsed ? ['Product Management'] : []"
7276
>
73-
<a-sub-menu key="Product Management">
77+
<SubMenu key="tab1">
7478
<template #icon><Icon icon="ph:tag-duotone" /></template>
75-
<template #title>Quản lý sản phẩm</template>
76-
<a-menu-item key="Product Management - List"> Danh sách sản phẩm </a-menu-item>
77-
<a-menu-item key="Product Management - Create"> Thêm sản phẩm </a-menu-item>
78-
</a-sub-menu>
79-
<a-menu-item key="Order Management">
79+
<template #title>Tab 1</template>
80+
<MenuItem :key="RoutePath.AdminTab1Sub1"> Tab1.1 </MenuItem>
81+
<MenuItem :key="RoutePath.AdminTab1Sub2"> Tab1.2 </MenuItem>
82+
</SubMenu>
83+
<MenuItem :key="RoutePath.AdminTab2">
8084
<template #icon>
8185
<Icon icon="ph:package-bold" />
8286
</template>
83-
Quản lý đơn hàng
84-
</a-menu-item>
85-
<a-menu-item key="User Management">
87+
Tab 2
88+
</MenuItem>
89+
<MenuItem :key="RoutePath.AdminTab3">
8690
<template #icon>
8791
<Icon icon="ph:user-bold" />
8892
</template>
89-
Quản lý người dùng
90-
</a-menu-item>
91-
</a-menu>
93+
Tab 3
94+
</MenuItem>
95+
</Menu>
9296

93-
<a-menu
97+
<Menu
9498
:style="{
9599
width: collapsed ? '64px' : '256px',
96100
marginTop: 'auto',
97101
marginBottom: '2rem'
98102
}"
99103
id="admin-side-logout"
100104
mode="inline"
101-
theme="dark"
105+
theme="light"
102106
:inline-collapsed="collapsed"
103107
:selected-keys="[]"
104108
>
105-
<a-menu-item key="Logout" @click="handleLogout">
109+
<MenuItem key="Logout" @click="handleLogout">
106110
<template #icon> <Icon icon="ph:sign-out" /> </template>
107111
Đăng xuất
108-
</a-menu-item>
109-
</a-menu>
112+
</MenuItem>
113+
</Menu>
110114
</div>
111115
</template>
112116

src/layouts/admin/index.vue

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script setup lang="ts">
22
import AdminSidebar from './AdminSidebar.vue'
3-
defineProps<{}>()
43
</script>
54

65
<template>

src/layouts/landing-page/LandingPageNavbar.vue

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Icon } from '@iconify/vue'
33
import { useAuthStore } from '@/stores/auth'
44
import { RouterLink } from 'vue-router'
5+
import { RoutePath } from '@/router'
56
67
const auth = useAuthStore()
78
</script>
@@ -80,9 +81,9 @@ const auth = useAuthStore()
8081
</nav>
8182
<div>
8283
<div v-if="!auth.isLoggedIn" class="hidden lg:flex items-center gap-4">
83-
<RouterLink to="/login">Log in</RouterLink>
84+
<RouterLink :to="RoutePath.Login">Log in</RouterLink>
8485
<RouterLink
85-
to="/register"
86+
:to="RoutePath.Register"
8687
class="rounded text-center transition focus-visible:ring-2 ring-offset-2 ring-gray-200 px-4 py-2 bg-vue text-white hover:bg-vue-shade border-2 border-transparent"
8788
>Sign up
8889
</RouterLink>

0 commit comments

Comments
 (0)