This commit is contained in:
ditiqi 2025-01-26 09:28:38 +08:00
parent daae6a9018
commit a0447eab2f
11 changed files with 552 additions and 294 deletions

View File

@ -38,7 +38,7 @@ export class BaseService<
protected prisma: PrismaClient,
protected objectType: string,
protected enableOrder: boolean = false,
) { }
) {}
/**
* Retrieves the name of the model dynamically.
@ -451,7 +451,11 @@ export class BaseService<
pageSize?: number;
where?: WhereArgs<A['findMany']>;
select?: SelectArgs<A['findMany']>;
}): Promise<{ items: R['findMany']; totalPages: number, totalCount: number }> {
}): Promise<{
items: R['findMany'];
totalPages: number;
totalCount: number;
}> {
const { page = 1, pageSize = 10, where, select } = args;
try {
@ -470,7 +474,7 @@ export class BaseService<
return {
items,
totalPages,
totalCount: total
totalCount: total,
};
} catch (error) {
this.handleError(error, 'read');

View File

@ -0,0 +1,74 @@
export function Header() {
return (
<header className="bg-gradient-to-r from-primary to-primary-400 p-6 rounded-t-xl">
<div className="flex flex-col space-y-6">
{/* 主标题区域 */}
<div>
<h1 className="text-3xl font-bold tracking-wider text-white">
</h1>
<p className="mt-2 text-blue-100 text-lg">
</p>
</div>
{/* 服务特点说明 */}
<div className="flex flex-wrap gap-6 text-sm text-white">
<div className="flex items-center gap-2">
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
/>
</svg>
<span></span>
</div>
<div className="flex items-center gap-2">
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
<span></span>
</div>
<div className="flex items-center gap-2">
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
/>
</svg>
<span></span>
</div>
</div>
{/* 服务宗旨说明 */}
<div className="text-sm text-blue-100 border-t border-blue-400/30 pt-4">
<p className="leading-relaxed">
</p>
</div>
</div>
</header>
);
}

View File

@ -0,0 +1,24 @@
import LetterList from "@web/src/components/models/post/list/LetterList";
import { Header } from "./Header";
import { useAuth } from "@web/src/providers/auth-provider";
export default function InboxPage() {
const { user } = useAuth();
return (
// 添加 flex flex-col 使其成为弹性布局容器
<div className="min-h-screen shadow-elegant border-2 border-white rounded-xl overflow-hidden bg-gradient-to-b from-slate-100 to-slate-50 flex flex-col">
<Header />
{/* 添加 flex-grow 使内容区域自动填充剩余空间 */}
<LetterList
params={{
where: {
receivers: {
some: {
id: user?.id,
},
},
},
}}></LetterList>
</div>
);
}

View File

@ -0,0 +1,11 @@
import { useAuth } from "@web/src/providers/auth-provider";
import InboxPage from "../inbox/page";
import LetterListPage from "../list/page";
export default function IndexPage() {
const { user } = useAuth();
if (user) {
return <InboxPage></InboxPage>;
}
return <LetterListPage></LetterListPage>;
}

View File

@ -0,0 +1,74 @@
export function Header() {
return (
<header className="bg-gradient-to-r from-primary to-primary-400 p-6 rounded-t-xl">
<div className="flex flex-col space-y-6">
{/* 主标题区域 */}
<div>
<h1 className="text-3xl font-bold tracking-wider text-white">
</h1>
<p className="mt-2 text-blue-100 text-lg">
</p>
</div>
{/* 服务特点说明 */}
<div className="flex flex-wrap gap-6 text-sm text-white">
<div className="flex items-center gap-2">
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
/>
</svg>
<span></span>
</div>
<div className="flex items-center gap-2">
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span></span>
</div>
<div className="flex items-center gap-2">
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
/>
</svg>
<span></span>
</div>
</div>
{/* 服务宗旨说明 */}
<div className="text-sm text-blue-100 border-t border-blue-400/30 pt-4">
<p className="leading-relaxed">
</p>
</div>
</div>
</header>
);
}

View File

@ -0,0 +1,20 @@
import LetterList from "@web/src/components/models/post/list/LetterList";
import { Header } from "./Header";
import { useAuth } from "@web/src/providers/auth-provider";
export default function OutboxPage() {
const { user } = useAuth();
return (
// 添加 flex flex-col 使其成为弹性布局容器
<div className="min-h-screen shadow-elegant border-2 border-white rounded-xl overflow-hidden bg-gradient-to-b from-slate-100 to-slate-50 flex flex-col">
<Header />
{/* 添加 flex-grow 使内容区域自动填充剩余空间 */}
<LetterList
params={{
where: {
authorId: user?.id,
},
}}></LetterList>
</div>
);
}

View File

@ -22,6 +22,7 @@ export default function WriteLetterPage() {
const { data, isLoading, error } = api.staff.findManyWithPagination.useQuery({
page: currentPage,
pageSize,
where: {
deptId: selectedDept,
OR: [{

View File

@ -4,194 +4,196 @@ import { motion, AnimatePresence } from "framer-motion";
import { useState, useRef, useCallback, useMemo } from "react";
import { Avatar } from "../../common/element/Avatar";
import {
UserOutlined,
SettingOutlined,
QuestionCircleOutlined,
LogoutOutlined
UserOutlined,
SettingOutlined,
QuestionCircleOutlined,
LogoutOutlined,
} from "@ant-design/icons";
import { Spin } from "antd";
import { useNavigate } from "react-router-dom";
import { MenuItemType } from "./types";
const menuVariants = {
hidden: { opacity: 0, scale: 0.95, y: -10 },
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: {
type: "spring",
stiffness: 300,
damping: 30
}
},
exit: {
opacity: 0,
scale: 0.95,
y: -10,
transition: {
duration: 0.2
}
}
hidden: { opacity: 0, scale: 0.95, y: -10 },
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: {
type: "spring",
stiffness: 300,
damping: 30,
},
},
exit: {
opacity: 0,
scale: 0.95,
y: -10,
transition: {
duration: 0.2,
},
},
};
export function UserMenu() {
const [showMenu, setShowMenu] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const { user, logout, isLoading } = useAuth();
const navigate = useNavigate()
useClickOutside(menuRef, () => setShowMenu(false));
const [showMenu, setShowMenu] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const { user, logout, isLoading } = useAuth();
const navigate = useNavigate();
useClickOutside(menuRef, () => setShowMenu(false));
const toggleMenu = useCallback(() => {
setShowMenu(prev => !prev);
}, []);
const toggleMenu = useCallback(() => {
setShowMenu((prev) => !prev);
}, []);
const menuItems: MenuItemType[] = useMemo(() => [
{
icon: <UserOutlined className="text-lg" />,
label: '个人信息',
action: () => { },
},
{
icon: <SettingOutlined className="text-lg" />,
label: '设置',
action: () => {
navigate('/admin/staff')
},
},
{
icon: <QuestionCircleOutlined className="text-lg" />,
label: '帮助',
action: () => { },
},
{
icon: <LogoutOutlined className="text-lg" />,
label: '注销',
action: () => logout(),
},
], [logout]);
const menuItems: MenuItemType[] = useMemo(
() => [
{
icon: <UserOutlined className="text-lg" />,
label: "个人信息",
action: () => {},
},
{
icon: <SettingOutlined className="text-lg" />,
label: "设置",
action: () => {
navigate("/admin/staff");
},
},
// {
// icon: <QuestionCircleOutlined className="text-lg" />,
// label: '帮助',
// action: () => { },
// },
{
icon: <LogoutOutlined className="text-lg" />,
label: "注销",
action: () => logout(),
},
],
[logout]
);
const handleMenuItemClick = useCallback((action: () => void) => {
action();
setShowMenu(false);
}, []);
const handleMenuItemClick = useCallback((action: () => void) => {
action();
setShowMenu(false);
}, []);
if (isLoading) {
return (
<div className="flex items-center justify-center w-10 h-10">
<Spin size="small" />
</div>
);
}
if (isLoading) {
return (
<div className="flex items-center justify-center w-10 h-10">
<Spin size="small" />
</div>
);
}
return (
<div ref={menuRef} className="relative">
<motion.button
aria-label="用户菜单"
aria-haspopup="true"
aria-expanded={showMenu}
aria-controls="user-menu"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
onClick={toggleMenu}
className="relative rounded-full focus:outline-none
return (
<div ref={menuRef} className="relative">
<motion.button
aria-label="用户菜单"
aria-haspopup="true"
aria-expanded={showMenu}
aria-controls="user-menu"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
onClick={toggleMenu}
className="relative rounded-full focus:outline-none
focus:ring-2 focus:ring-[#00538E]/80 focus:ring-offset-2
focus:ring-offset-white transition-all duration-200 ease-in-out"
>
<Avatar
src={user?.avatar}
name={user?.showname || user?.username}
size={40}
className="ring-2 ring-white hover:ring-[#00538E]/90
focus:ring-offset-white transition-all duration-200 ease-in-out">
<Avatar
src={user?.avatar}
name={user?.showname || user?.username}
size={40}
className="ring-2 ring-white hover:ring-[#00538E]/90
transition-all duration-200 ease-in-out shadow-md
hover:shadow-lg"
/>
<span
className="absolute bottom-0 right-0 h-3 w-3
/>
<span
className="absolute bottom-0 right-0 h-3 w-3
rounded-full bg-emerald-500 ring-2 ring-white
shadow-sm transition-transform duration-200
ease-in-out hover:scale-110"
aria-hidden="true"
/>
</motion.button>
aria-hidden="true"
/>
</motion.button>
<AnimatePresence>
{showMenu && (
<motion.div
initial="hidden"
animate="visible"
exit="exit"
variants={menuVariants}
role="menu"
id="user-menu"
aria-orientation="vertical"
aria-labelledby="user-menu-button"
style={{ zIndex: 100 }}
className="absolute right-0 mt-3 w-64 origin-top-right
<AnimatePresence>
{showMenu && (
<motion.div
initial="hidden"
animate="visible"
exit="exit"
variants={menuVariants}
role="menu"
id="user-menu"
aria-orientation="vertical"
aria-labelledby="user-menu-button"
style={{ zIndex: 100 }}
className="absolute right-0 mt-3 w-64 origin-top-right
bg-white rounded-xl overflow-hidden shadow-lg
border border-[#E5EDF5]"
>
{/* User Profile Section */}
<div
className="px-4 py-4 bg-gradient-to-b from-[#F6F9FC] to-white
border-b border-[#E5EDF5] "
border border-[#E5EDF5]">
{/* User Profile Section */}
<div
className="px-4 py-4 bg-gradient-to-b from-[#F6F9FC] to-white
border-b border-[#E5EDF5] ">
<div className="flex items-center space-x-4">
<Avatar
src={user?.avatar}
name={user?.showname || user?.username}
size={40}
className="ring-2 ring-white shadow-sm"
/>
<div className="flex flex-col space-y-0.5">
<span className="text-sm font-semibold text-[#00538E]">
{user?.showname || user?.username}
</span>
<span className="text-xs text-[#718096] flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span>
线
</span>
</div>
</div>
</div>
>
<div className="flex items-center space-x-4">
<Avatar
src={user?.avatar}
name={user?.showname || user?.username}
size={40}
className="ring-2 ring-white shadow-sm"
/>
<div className="flex flex-col space-y-0.5">
<span className="text-sm font-semibold text-[#00538E]">
{user?.showname || user?.username}
</span>
<span className="text-xs text-[#718096] flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span>
线
</span>
</div>
</div>
</div>
{/* Menu Items */}
<div className="p-2">
{menuItems.map((item, index) => (
<button
key={index}
role="menuitem"
tabIndex={showMenu ? 0 : -1}
onClick={(e) => {
e.stopPropagation();
handleMenuItemClick(item.action);
}}
className={`flex items-center gap-3 w-full px-4 py-3
{/* Menu Items */}
<div className="p-2">
{menuItems.map((item, index) => (
<button
key={index}
role="menuitem"
tabIndex={showMenu ? 0 : -1}
onClick={(e) => {
e.stopPropagation();
handleMenuItemClick(item.action);
}}
className={`flex items-center gap-3 w-full px-4 py-3
text-sm font-medium rounded-lg transition-all
focus:outline-none
focus:ring-2 focus:ring-[#00538E]/20
group relative overflow-hidden
active:scale-[0.99]
${item.label === '注销'
? 'text-[#B22234] hover:bg-red-50/80 hover:text-red-700'
: 'text-[#00538E] hover:bg-[#E6EEF5] hover:text-[#003F6A]'
}`}
>
<span className={`w-5 h-5 flex items-center justify-center
${
item.label === "注销"
? "text-[#B22234] hover:bg-red-50/80 hover:text-red-700"
: "text-[#00538E] hover:bg-[#E6EEF5] hover:text-[#003F6A]"
}`}>
<span
className={`w-5 h-5 flex items-center justify-center
transition-all duration-200 ease-in-out
group-hover:scale-110 group-hover:rotate-6
group-hover:translate-x-0.5 ${item.label === '注销'
? 'group-hover:text-red-600'
: 'group-hover:text-[#003F6A]'}`}>
{item.icon}
</span>
<span>{item.label}</span>
</button>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
group-hover:translate-x-0.5 ${
item.label === "注销"
? "group-hover:text-red-600"
: "group-hover:text-[#003F6A]"
}`}>
{item.icon}
</span>
<span>{item.label}</span>
</button>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}

View File

@ -1,96 +1,108 @@
import { NavLink, useLocation } from "react-router-dom";
import { useNavItem } from "./useNavItem";
import { twMerge } from "tailwind-merge";
import React from "react";
interface NavItem {
to: string;
label: string;
icon?: React.ReactNode;
to: string;
label: string;
icon?: React.ReactNode;
}
interface NavigationProps {
className?: string;
className?: string;
}
export default function Navigation({ className }: NavigationProps) {
const { navItems } = useNavItem();
const location = useLocation();
const { navItems } = useNavItem();
const location = useLocation();
const isActive = (to: string) => {
const [pathname, search] = to.split('?');
return location.pathname === pathname &&
(!search ? !location.search : location.search === `?${search}`);
};
const isActive = (to: string) => {
const [pathname, search] = to.split("?");
return (
location.pathname === pathname &&
(!search ? !location.search : location.search === `?${search}`)
);
};
return (
<nav className={twMerge(
"mt-4 rounded-xl bg-gradient-to-r from-primary-500 to-primary-600 shadow-lg",
className
)}>
<div className="flex flex-col md:flex-row items-stretch">
{/* Desktop Navigation */}
<div className="hidden md:flex items-center px-6 py-2 w-full overflow-x-auto">
<div className="flex space-x-6 min-w-max">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive: active }) => twMerge(
"relative px-4 py-2.5 text-sm font-medium",
"text-gray-300 hover:text-white",
"transition-all duration-200 ease-out group",
active && "text-white"
)}
>
<span className="relative z-10 flex items-center gap-2 transition-transform group-hover:translate-y-[-1px]">
{item.icon}
<span className="tracking-wide">{item.label}</span>
</span>
return (
<nav
className={twMerge(
"mt-4 rounded-xl bg-gradient-to-r from-primary-500 to-primary-600 shadow-lg",
className
)}>
<div className="flex flex-col md:flex-row items-stretch">
{/* Desktop Navigation */}
<div className="hidden md:flex items-center px-6 py-2 w-full overflow-x-auto">
<div className="flex space-x-6 min-w-max">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive: active }) =>
twMerge(
"relative px-4 py-2.5 text-sm font-medium",
"text-gray-300 hover:text-white",
"transition-all duration-200 ease-out group",
active && "text-white"
)
}>
<span className="relative z-10 flex items-center gap-2 transition-transform group-hover:translate-y-[-1px]">
{item.icon}
<span className="tracking-wide">
{item.label}
</span>
</span>
{/* Active Indicator */}
<span className={twMerge(
"absolute bottom-0 left-1/2 h-[2px] bg-blue-400",
"transition-all duration-300 ease-out",
"transform -translate-x-1/2",
isActive(item.to)
? "w-full opacity-100"
: "w-0 opacity-0 group-hover:w-1/2 group-hover:opacity-40"
)} />
{/* Active Indicator */}
<span
className={twMerge(
"absolute bottom-0 left-1/2 h-[2px] bg-blue-400",
"transition-all duration-300 ease-out",
"transform -translate-x-1/2",
isActive(item.to)
? "w-full opacity-100"
: "w-0 opacity-0 group-hover:w-1/2 group-hover:opacity-40"
)}
/>
{/* Hover Glow Effect */}
<span className={twMerge(
"absolute inset-0 rounded-lg bg-blue-400/0",
"transition-all duration-300",
"group-hover:bg-blue-400/5"
)} />
</NavLink>
))}
</div>
</div>
{/* Hover Glow Effect */}
<span
className={twMerge(
"absolute inset-0 rounded-lg bg-blue-400/0",
"transition-all duration-300",
"group-hover:bg-blue-400/5"
)}
/>
</NavLink>
))}
</div>
</div>
{/* Mobile Navigation */}
<div className="md:hidden flex overflow-x-auto scrollbar-none px-4 py-2">
<div className="flex space-x-4 min-w-max">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive: active }) => twMerge(
"px-3 py-1.5 text-sm font-medium rounded-full",
"transition-colors duration-200",
"text-gray-300 hover:text-white",
active && "bg-blue-500/20 text-white"
)}
>
<span className="flex items-center gap-1.5">
{item.icon}
<span>{item.label}</span>
</span>
</NavLink>
))}
</div>
</div>
</div>
</nav>
);
{/* Mobile Navigation */}
<div className="md:hidden flex overflow-x-auto scrollbar-none px-4 py-2">
<div className="flex space-x-4 min-w-max">
{navItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive: active }) =>
twMerge(
"px-3 py-1.5 text-sm font-medium rounded-full",
"transition-colors duration-200",
"text-gray-300 hover:text-white",
active && "bg-blue-500/20 text-white"
)
}>
<span className="flex items-center gap-1.5">
{item.icon}
<span>{item.label}</span>
</span>
</NavLink>
))}
</div>
</div>
</div>
</nav>
);
}

View File

@ -1,66 +1,87 @@
import { api } from "@nice/client";
import { TaxonomySlug } from "@nice/common";
import { useMemo } from "react";
import React, { useMemo } from "react";
import { MailOutlined, SendOutlined } from "@ant-design/icons";
import {
FileTextOutlined,
ScheduleOutlined,
QuestionCircleOutlined,
FolderOutlined,
TagsOutlined
FileTextOutlined,
ScheduleOutlined,
QuestionCircleOutlined,
FolderOutlined,
TagsOutlined,
} from "@ant-design/icons";
import { useAuth } from "@web/src/providers/auth-provider";
export interface NavItem {
to: string;
label: string;
icon?: React.ReactNode;
to: string;
label: string;
icon?: React.ReactNode;
}
export function useNavItem() {
const { data } = api.term.findMany.useQuery({
where: {
taxonomy: { slug: TaxonomySlug.CATEGORY }
}
});
const { user } = useAuth();
const { data } = api.term.findMany.useQuery({
where: {
taxonomy: { slug: TaxonomySlug.CATEGORY },
},
});
const navItems = useMemo(() => {
// 定义固定的导航项
const staticItems = {
letterList: {
to: "/",
label: "公开信件",
icon: <FileTextOutlined className="text-base" />
},
letterProgress: {
to: "/letter-progress",
label: "进度查询",
icon: <ScheduleOutlined className="text-base" />
},
help: {
to: "/help",
label: "使用帮助",
icon: <QuestionCircleOutlined className="text-base" />
}
};
const navItems = useMemo(() => {
// 定义固定的导航项
const staticItems = {
inbox: {
to: user ? "/" : "/inbox",
label: "我收到的",
icon: <MailOutlined className="text-base" />,
},
outbox: {
to: "/outbox",
label: "我发出的",
icon: <SendOutlined className="text-base" />,
},
letterList: {
to: !user ? "/" : "/letter-list",
label: "公开信件",
icon: <FileTextOutlined className="text-base" />,
},
letterProgress: {
to: "/letter-progress",
label: "进度查询",
icon: <ScheduleOutlined className="text-base" />,
},
// help: {
// to: "/help",
// label: "使用帮助",
// icon: <QuestionCircleOutlined className="text-base" />
// }
};
if (!data) {
return [staticItems.letterList, staticItems.letterProgress, staticItems.help];
}
if (!data) {
return [
user && staticItems.inbox,
user && staticItems.outbox,
staticItems.letterList,
staticItems.letterProgress,
// staticItems.help,
].filter(Boolean);
}
// 构建分类导航项
const categoryItems = data.map(term => ({
to: `/write-letter?termId=${term.id}`,
label: term.name,
icon: <TagsOutlined className="text-base"></TagsOutlined>
}));
// 构建分类导航项
const categoryItems = data.map((term) => ({
to: `/write-letter?termId=${term.id}`,
label: term.name,
icon: <TagsOutlined className="text-base"></TagsOutlined>,
}));
// 按照指定顺序返回导航项
return [
staticItems.letterList,
staticItems.letterProgress,
...categoryItems,
staticItems.help
];
}, [data]);
// 按照指定顺序返回导航项
return [
user && staticItems.inbox,
user && staticItems.outbox,
staticItems.letterList,
staticItems.letterProgress,
...categoryItems,
// staticItems.help,
].filter(Boolean);
}, [data]);
return { navItems };
return { navItems };
}

View File

@ -23,6 +23,9 @@ import LetterDetailPage from "../app/main/letter/detail/page";
import AdminLayout from "../components/layout/admin/AdminLayout";
import { CustomRouteObject } from "./types";
import { adminRoute } from "./admin-route";
import InboxPage from "../app/main/letter/inbox/page";
import OutboxPage from "../app/main/letter/outbox/page";
import IndexPage from "../app/main/letter/index/page";
export const routes: CustomRouteObject[] = [
{
path: "/",
@ -36,8 +39,20 @@ export const routes: CustomRouteObject[] = [
{
element: <MainLayout></MainLayout>,
children: [
{
path: "inbox",
element: <InboxPage></InboxPage>,
},
{
path: "outbox",
element: <OutboxPage></OutboxPage>,
},
{
index: true,
element: <IndexPage></IndexPage>,
},
{
path: "letter-list",
element: <LetterListPage></LetterListPage>,
},
{