This commit is contained in:
longdayi 2025-01-22 19:24:52 +08:00
parent cac7fea6e4
commit fe4e6fbf3a
21 changed files with 945 additions and 152 deletions

View File

@ -67,6 +67,7 @@
"react-resizable": "^3.0.5",
"react-router-dom": "^6.24.1",
"superjson": "^2.2.1",
"swiper": "^11.2.1",
"tailwind-merge": "^2.6.0",
"uuid": "^10.0.0",
"yjs": "^13.6.20",

View File

@ -39,4 +39,27 @@
.ag-root-wrapper {
border: 0px;
}
/* styles/carousel.css */
.swiper-button-next,
.swiper-button-prev {
@apply text-white opacity-0 transition-opacity duration-300;
}
.swiper-button-disabled {
@apply opacity-0 cursor-not-allowed;
}
.swiper:hover .swiper-button-next,
.swiper:hover .swiper-button-prev {
@apply opacity-70 hover:opacity-100;
}
.swiper-pagination-bullet {
@apply bg-white/70;
}
.swiper-pagination-bullet-active {
@apply bg-white;
}

View File

@ -1,10 +1,26 @@
import GraphEditor from '@web/src/components/common/editor/graph/GraphEditor';
import { Carousel } from '@web/src/components/common/element/Carousel';
import LeaderCard from '@web/src/components/common/element/LeaderCard';
import React, { useState, useCallback } from 'react';
import * as tus from 'tus-js-client';
interface TusUploadProps {
onSuccess?: (response: any) => void;
onError?: (error: Error) => void;
}
const carouselItems = [
{
id: 1,
image: 'https://th.bing.com/th/id/OIP.PEtRTLQwIX54HGFX5xNDYwHaE7?rs=1&pid=ImgDetMain',
title: '自然风光',
description: '壮丽的山川河流'
},
{
id: 2,
image: 'https://eskipaper.com/images/scenery-pictures-15.jpg',
title: '城市景观',
description: '现代化的都市风貌'
}
];
const TusUploader: React.FC<TusUploadProps> = ({
onSuccess,
onError
@ -49,15 +65,21 @@ const TusUploader: React.FC<TusUploadProps> = ({
}, [onSuccess, onError]);
return (
<div>
<div className='w-full' style={{ height: 800 }}>
<GraphEditor></GraphEditor>
</div>
{/* <div className=' h-screen'>
<MindMap></MindMap>
</div> */}
{/* <MindMapEditor></MindMapEditor> */}
<div className=' flex flex-col gap-4'>
<Carousel
slides={carouselItems}
autoplayDelay={5000}
className="shadow-xl"
/>
<LeaderCard
name="张三"
title="技术总监"
description={
"负责公司技术战略规划"
}
imageUrl="https://th.bing.com/th/id/OIP.ea0spF2OAgI4I1KzgZFtTgHaHX?rs=1&pid=ImgDetMain"
/>
<input
type="file"
onChange={(e) => {

View File

@ -0,0 +1,118 @@
import { useState } from 'react';
import {
MagnifyingGlassIcon,
StarIcon,
TrashIcon,
EnvelopeIcon,
FunnelIcon,
} from '@heroicons/react/24/outline';
import { StarIcon as StarIconSolid } from '@heroicons/react/24/solid';
interface Letter {
id: string;
sender: string;
subject: string;
date: string;
priority: 'high' | 'medium' | 'low';
isRead: boolean;
isStarred: boolean;
}
export default function LetterListPage() {
const [letters, setLetters] = useState<Letter[]>([
{
id: '1',
sender: 'Gen. Charles Q. Brown Jr.',
subject: 'Strategic Force Posture Update',
date: '2024-01-22',
priority: 'high',
isRead: false,
isStarred: true,
},
// ... existing code ...
]);
const [searchTerm, setSearchTerm] = useState('');
const [selectedFilter, setSelectedFilter] = useState('all');
return (
<div className="min-h-screen bg-gray-50">
{/* Header */}
<header className="bg-[#00538B] text-white p-4 shadow-lg">
<div className="container mx-auto flex items-center gap-2">
<EnvelopeIcon className="w-8 h-8" />
<h1 className="text-2xl font-bold">USAF Leadership Mailbox</h1>
</div>
</header>
{/* Main Content */}
<main className="container mx-auto py-6 px-4">
{/* Search and Filter Bar */}
<div className="flex gap-4 mb-6">
<div className="flex-1 relative">
<MagnifyingGlassIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<input
type="text"
placeholder="Search letters..."
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00538B] focus:border-transparent"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="relative">
<FunnelIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<select
className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00538B] appearance-none bg-white"
value={selectedFilter}
onChange={(e) => setSelectedFilter(e.target.value)}
>
<option value="all">All Letters</option>
<option value="unread">Unread</option>
<option value="starred">Starred</option>
<option value="high">High Priority</option>
</select>
</div>
</div>
{/* Letters List */}
<div className="bg-white rounded-lg shadow">
{letters.map((letter) => (
<div
key={letter.id}
className={`flex items-center p-4 border-b border-gray-100 hover:bg-gray-50 cursor-pointer transition-colors ${!letter.isRead ? 'font-semibold bg-blue-50' : ''
}`}
>
<div className="flex items-center gap-4 flex-1">
<button
className="focus:outline-none"
onClick={() => {/* Toggle star */ }}
>
{letter.isStarred ? (
<StarIconSolid className="w-6 h-6 text-yellow-400" />
) : (
<StarIcon className="w-6 h-6 text-gray-400 hover:text-yellow-400" />
)}
</button>
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="text-gray-900">{letter.sender}</span>
{letter.priority === 'high' && (
<span className="px-2 py-1 text-xs bg-red-100 text-red-800 rounded-full">
High Priority
</span>
)}
</div>
<div className="text-gray-600">{letter.subject}</div>
</div>
<div className="text-sm text-gray-500">{letter.date}</div>
<button className="p-1 rounded-full hover:bg-gray-100 focus:outline-none">
<TrashIcon className="w-5 h-5 text-gray-400 hover:text-red-500" />
</button>
</div>
</div>
))}
</div>
</main>
</div>
);
}

View File

@ -0,0 +1,3 @@
export default function LetterSearchPage() {
return <>Search</>
}

View File

@ -0,0 +1,28 @@
import { Leader } from "./types";
export const leaders: Leader[] = [
{
id: '1',
name: 'John Mitchell',
rank: 'General',
division: 'Air Combat Command',
imageUrl: 'https://th.bing.com/th/id/OIP.ea0spF2OAgI4I1KzgZFtTgHaHX?rs=1&pid=ImgDetMain',
email: 'j.mitchell@af.mil',
phone: '(555) 123-4567',
office: 'Pentagon, Wing A-123',
},
{
id: '2',
name: 'Sarah Williams',
rank: 'Colonel',
division: 'Air Force Space Command',
imageUrl: 'https://th.bing.com/th/id/OIP.ea0spF2OAgI4I1KzgZFtTgHaHX?rs=1&pid=ImgDetMain',
},
{
id: '3',
name: 'Michael Roberts',
rank: 'Major General',
division: 'Air Mobility Command',
imageUrl: 'https://th.bing.com/th/id/OIP.ea0spF2OAgI4I1KzgZFtTgHaHX?rs=1&pid=ImgDetMain',
},
];

View File

@ -0,0 +1,200 @@
import { useState, useMemo } from 'react';
import { motion } from 'framer-motion';
import { FunnelIcon, MagnifyingGlassIcon, PaperAirplaneIcon } from '@heroicons/react/24/outline';
import { Leader } from './types';
import { leaders } from './mock';
export default function WriteLetterPage() {
const [selectedLeader, setSelectedLeader] = useState<Leader | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [selectedDivision, setSelectedDivision] = useState<string>('all');
const divisions = useMemo(() => {
return ['all', ...new Set(leaders.map(leader => leader.division))];
}, []);
const filteredLeaders = useMemo(() => {
return leaders.filter(leader => {
const matchesSearch = leader.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
leader.rank.toLowerCase().includes(searchQuery.toLowerCase());
const matchesDivision = selectedDivision === 'all' || leader.division === selectedDivision;
return matchesSearch && matchesDivision;
});
}, [searchQuery, selectedDivision]);
return (
<div className="min-h-screen bg-gradient-to-b from-slate-100 to-slate-200">
{/* Header Banner */}
<div className="bg-gradient-to-r from-[#00308F] to-[#0353A4] text-white py-8">
<div className="container mx-auto px-4">
<div className="flex flex-col space-y-6">
{/* 主标题 */}
<div>
<h1 className="text-3xl font-bold tracking-wider">
</h1>
<p className="mt-2 text-blue-100 text-lg">
</p>
</div>
{/* 隐私保护说明 */}
<div className="flex flex-wrap gap-6 text-sm">
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</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="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</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 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z" />
</svg>
<span></span>
</div>
</div>
{/* 隐私承诺 */}
<div className="text-sm text-blue-100 border-t border-blue-400/30 pt-4">
<p></p>
</div>
</div>
</div>
</div>
{/* 搜索和筛选区域 */}
<div className="container mx-auto px-4 py-8">
<div className="flex flex-col md:flex-row gap-4 mb-8">
<div className="relative flex-1">
<MagnifyingGlassIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<input
type="text"
placeholder="Search by name or rank..."
className="w-full pl-10 pr-4 py-3 rounded-lg border border-gray-200 focus:ring-2 focus:ring-[#00308F] focus:border-transparent"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<div className="relative">
<FunnelIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<select
className="pl-10 pr-8 py-3 rounded-lg border border-gray-200 focus:ring-2 focus:ring-[#00308F] appearance-none bg-white"
value={selectedDivision}
onChange={(e) => setSelectedDivision(e.target.value)}
>
{divisions.map(division => (
<option key={division} value={division}>
{division === 'all' ? 'All Divisions' : division}
</option>
))}
</select>
</div>
</div>
{/* Modified Leader Cards Grid */}
<div className="grid grid-cols-1 gap-6">
{filteredLeaders.map((leader) => (
<motion.div
key={leader.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
whileHover={{ scale: 1.005 }}
transition={{ duration: 0.2 }}
className={`
bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100
${selectedLeader?.id === leader.id
? 'ring-2 ring-[#00308F]'
: 'hover:shadow-lg hover:border-blue-100'
}
`}
>
<div className="flex flex-col sm:flex-row">
{/* Image Container */}
<div className="sm:w-48 h-64 sm:h-auto flex-shrink-0">
<img
src={leader.imageUrl}
alt={leader.name}
className="w-full h-full object-cover"
/>
</div>
{/* Content Container */}
<div className="flex-1 p-6">
<div className="flex flex-col h-full">
<div className="mb-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-xl font-semibold text-gray-900">
{leader.name}
</h3>
<span className="px-3 py-1 text-sm font-medium bg-blue-50 text-[#00308F] rounded-full">
{leader.rank}
</span>
</div>
<p className="text-gray-600 mb-4">{leader.division}</p>
{/* Contact Information */}
<div className="space-y-2 text-sm text-gray-600">
<p className="flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
{leader.email}
</p>
<p className="flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{leader.phone}
</p>
<p className="flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{leader.office}
</p>
</div>
</div>
<button
onClick={() => setSelectedLeader(leader)}
className="mt-auto w-full sm:w-auto flex items-center justify-center gap-2
bg-[#00308F] text-white py-3 px-6 rounded-lg
hover:bg-[#002070] transition-all duration-300
focus:outline-none focus:ring-2 focus:ring-[#00308F] focus:ring-opacity-50
transform hover:-translate-y-0.5"
>
<PaperAirplaneIcon className="w-5 h-5" />
Compose Letter
</button>
</div>
</div>
</div>
</motion.div>
))}
</div>
{/* 无结果提示 */}
{filteredLeaders.length === 0 && (
<div className="text-center py-12">
<p className="text-gray-600 text-lg">
No leaders found matching your search criteria
</p>
</div>
)}
</div>
</div>
);
}

View File

@ -0,0 +1,12 @@
export interface Leader {
id: string;
name: string;
rank: string;
division: string;
imageUrl: string;
email: string; // Added
phone: string; // Added
office: string; // Added
}

View File

@ -0,0 +1,120 @@
import { motion, AnimatePresence } from 'framer-motion';
import { useEffect, useState, useRef } from 'react';
import { Swiper, SwiperRef, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination, Autoplay } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
interface Slide {
id: number;
image: string;
title?: string;
description?: string;
}
interface CarouselProps {
slides: Slide[];
autoplayDelay?: number;
className?: string;
swiperConfig?: {
spaceBetween?: number;
slidesPerView?: number;
navigation?: boolean;
loop?: boolean;
pagination?: boolean | { clickable: boolean };
autoplayOptions?: {
delay: number;
disableOnInteraction: boolean;
};
};
}
export const Carousel = ({
slides,
autoplayDelay = 3000,
className = '',
swiperConfig = {},
}: CarouselProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const swiperRef = useRef<any>(null);
const {
spaceBetween = 0,
slidesPerView = 1,
navigation = true,
loop = true,
pagination = { clickable: true },
autoplayOptions = {
delay: autoplayDelay,
disableOnInteraction: false,
},
} = swiperConfig;
return (
<div className={`relative overflow-hidden ${className}`}>
<Swiper
modules={[Navigation, Pagination, Autoplay]}
spaceBetween={spaceBetween}
slidesPerView={slidesPerView}
navigation={navigation}
loop={loop}
pagination={pagination}
autoplay={autoplayOptions}
onSlideChange={(swiper) => setActiveIndex(swiper.realIndex)}
onSwiper={(swiper) => (swiperRef.current = swiper)}
className="w-full h-full rounded-xl"
>
{slides.map((slide) => (
<SwiperSlide key={slide.id}>
<div className="relative w-full h-[400px] md:h-[600px]">
<img
src={slide.image}
alt={slide.title || `Slide ${slide.id}`}
className="w-full h-full object-cover"
/>
<AnimatePresence>
{(slide.title || slide.description) && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
className="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-black/60 to-transparent"
>
{slide.title && (
<h3 className="text-white text-2xl font-semibold mb-2">
{slide.title}
</h3>
)}
{slide.description && (
<p className="text-white/90 text-sm">
{slide.description}
</p>
)}
</motion.div>
)}
</AnimatePresence>
</div>
</SwiperSlide>
))}
</Swiper>
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 z-10 flex gap-2">
{slides.map((_, index) => (
<button
key={index}
className={`w-2 h-2 rounded-full transition-all duration-300 ${index === activeIndex
? 'bg-white scale-125'
: 'bg-white/50 hover:bg-white/70'
}`}
onClick={() => {
if (swiperRef.current) {
swiperRef.current.slideToLoop(index); // 使用 Swiper 的 slideToLoop 方法切换到指定的幻灯片
}
}}
/>
))}
</div>
</div>
);
};

View File

@ -0,0 +1,74 @@
import React from "react";
import { Button } from "./Button";
type LeaderCardProps = {
name: string;
title: string;
description: string;
imageUrl: string;
onClick?: () => void; // 添加整个卡片的点击事件
};
const LeaderCard: React.FC<LeaderCardProps> = ({
name,
title,
description,
imageUrl,
onClick,
}) => {
return (
<div
className="group flex bg-white rounded-2xl shadow-md hover:shadow-2xl transition-all duration-300 ease-out max-w-2xl border border-gray-100 hover:border-blue-100 cursor-pointer overflow-hidden"
onClick={onClick}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
onClick?.();
}
}}
>
{/* Image Section */}
<div className="relative flex-shrink-0 overflow-hidden">
<img
src={imageUrl}
alt={name}
className="w-44 h-64 object-cover transition-transform duration-500 group-hover:scale-105"
style={{
aspectRatio: "3/4",
}}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
</div>
{/* Content Section */}
<div className="flex flex-col justify-between p-5 flex-1">
<div className="space-y-3">
{/* Name and Title */}
<div className="transform transition-transform duration-300 group-hover:translate-x-2">
<h3 className="text-2xl font-semibold text-gray-800 mb-1 tracking-tight group-hover:text-blue-600 transition-colors">
{name}
</h3>
<p className="text-base font-medium text-blue-600 tracking-wide group-hover:text-blue-500">
{title}
</p>
</div>
{/* Description */}
<p className="text-gray-600 text-base leading-relaxed line-clamp-4 font-light group-hover:text-gray-700">
{description}
</p>
</div>
{/* Decorative Element */}
<div className="mt-4">
<div className="h-0.5 w-8 bg-blue-600 rounded-full transform origin-left transition-all duration-300 group-hover:w-16 group-hover:bg-gradient-to-r from-blue-600 to-blue-400" />
</div>
</div>
</div>
);
};
export default LeaderCard;

View File

@ -53,7 +53,7 @@ const FileItem = memo(({ file, progress, onRemove }: {
))
export default function FileUploader({
endpoint='',
endpoint = '',
onSuccess,
onError,
maxSize = 100,

View File

@ -0,0 +1,78 @@
import { PhoneIcon } from '@heroicons/react/24/outline'
export function Footer() {
return (
<footer className="bg-gradient-to-b from-[#13294B] to-[#0c1c33] text-gray-300">
<div className="container mx-auto px-6 py-10">
{/* Main Footer Content */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-10 mb-8">
{/* Logo and Main Info */}
<div className="text-center md:text-left">
<div className="flex md:justify-start justify-center mb-4 group">
<img
src="/usaf-emblem.png"
alt="USAF Official Emblem"
className="h-16 w-auto transform transition-all duration-300
group-hover:scale-105 group-hover:brightness-110
drop-shadow-lg"
/>
</div>
<h3 className="text-white font-bold text-lg tracking-wide mb-2
drop-shadow-md">
</h3>
<p className="text-gray-400 text-sm">
</p>
</div>
{/* Technical Support */}
<div className="text-center md:text-left md:pl-6 md:border-l border-gray-700">
<h4 className="text-white font-semibold mb-4 text-lg tracking-wide
drop-shadow-md">
</h4>
<div className="space-y-4">
<div className="bg-gradient-to-br from-[#1a3a6a] to-[#234785]
p-4 rounded-lg shadow-lg hover:shadow-xl
transition-all duration-300 transform hover:-translate-y-0.5">
<p className="text-white font-medium mb-2"> </p>
<p className="text-gray-300 text-sm"></p>
<div className="mt-3 flex items-center justify-center md:justify-start gap-2
text-sm text-gray-300">
<PhoneIcon className="w-4 h-4" />
<span>1-800-XXX-XXXX</span>
</div>
</div>
<p className="text-gray-400 text-sm italic">
24/7
</p>
</div>
</div>
</div>
{/* Divider */}
<div className="border-t border-gray-700/50 my-6"></div>
{/* Bottom Section */}
<div className="text-center">
<div className="flex flex-wrap justify-center gap-x-6 gap-y-2 mb-4 text-sm
text-gray-400">
<span className="hover:text-gray-300 transition-colors duration-200">
</span>
<span className="hover:text-gray-300 transition-colors duration-200">
</span>
<span className="hover:text-gray-300 transition-colors duration-200">
</span>
</div>
<p className="text-gray-500 text-sm">
© {new Date().getFullYear()} United States Air Force. All rights reserved.
</p>
</div>
</div>
</footer>
)
}

View File

@ -0,0 +1,85 @@
import { MagnifyingGlassIcon, UserIcon } from "@heroicons/react/24/outline";
import { Link, NavLink } from "react-router-dom";
import { memo } from "react";
import { NAV_ITEMS } from "./constants";
import { SearchBar } from "./SearchBar";
import { Logo } from "./Logo";
interface HeaderProps {
onSearch?: (query: string) => void;
}
export const Header = memo(function Header({ onSearch }: HeaderProps) {
return (
<header className="sticky top-0 z-50 bg-[#13294B] text-white shadow-lg">
<div className="container mx-auto px-4">
{/* Main Content */}
<div className="py-4">
<div className="flex items-center justify-between">
<Logo />
<SearchBar onSearch={onSearch} />
{/* User Actions */}
<div className="flex items-center space-x-6">
<Link
to="/login"
className="group flex items-center space-x-2 rounded-lg bg-[#00539B] px-5
py-2.5 shadow-lg transition-all duration-300
hover:-translate-y-0.5 hover:bg-[#0063B8]
hover:shadow-[#00539B]/50 focus:outline-none
focus:ring-2 focus:ring-[#8EADD4] focus:ring-offset-2"
aria-label="Login"
>
<UserIcon className="h-5 w-5 transition-transform group-hover:scale-110" />
<span className="font-medium">Login</span>
</Link>
</div>
</div>
</div>
{/* Navigation */}
<nav className="mt-4 rounded-lg bg-[#0B1A32]/90">
<div className="flex items-center justify-between px-6 py-1">
<div className="flex space-x-1">
{NAV_ITEMS.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) => `
group relative px-4 py-3 rounded-md
transition-all duration-300 ease-out
outline-none
${isActive ? 'text-white font-medium' : 'text-[#8EADD4]'}
`}
>
{({ isActive }) => (
<>
<span className="relative z-10 transition-colors group-hover:text-white">
{item.label}
</span>
<span className={`
absolute inset-0 rounded-md bg-gradient-to-b
from-white/10 to-transparent transition-opacity
duration-300 ease-out opacity-0 group-hover:opacity-10
${isActive ? 'opacity-5' : ''}
`} />
<span className={`
absolute bottom-1.5 left-1/2 h-[2px] bg-white
transition-all duration-300 ease-out
transform -translate-x-1/2
${isActive
? 'w-12 opacity-100'
: 'w-0 opacity-0 group-hover:w-8 group-hover:opacity-50'
}
`} />
</>
)}
</NavLink>
))}
</div>
</div>
</nav>
</div>
</header>
);
});

View File

@ -0,0 +1,16 @@
import { Link } from "react-router-dom";
export const Logo = () => (
<Link to="/" className="flex items-center space-x-3 group focus:outline-none focus:ring-2 focus:ring-[#8EADD4] rounded-lg">
<img
src="/logo.svg"
alt="USAF Logo"
className="h-12 w-auto transform transition-transform group-hover:scale-105"
loading="eager"
/>
<div>
<h1 className="text-2xl font-bold tracking-wider">Leadership Mailbox</h1>
<p className="text-sm text-[#8EADD4]">United States Air Force</p>
</div>
</Link>
);

View File

@ -1,40 +1,27 @@
import { useState, useEffect, useRef, ReactNode } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { TopNavBar } from '@web/src/components/layout/main/top-nav-bar';
import { navItems, notificationItems } from '@web/src/components/layout/main/nav-data';
import { Sidebar } from '@web/src/components/layout/main/side-bar';
import { Outlet } from 'react-router-dom';
import { motion } from 'framer-motion'
import { Outlet } from 'react-router-dom'
import { Header } from './Header'
import { Footer } from './Footer'
export function MainLayout() {
const [sidebarOpen, setSidebarOpen] = useState(true);
const [notifications, setNotifications] = useState(3);
const [recentSearches] = useState([
'React Fundamentals',
'TypeScript Advanced',
'Tailwind CSS Projects',
]);
return (
<div className="min-h-screen bg-gray-50">
<TopNavBar
sidebarOpen={sidebarOpen}
setSidebarOpen={setSidebarOpen}
notifications={notifications}
notificationItems={notificationItems}
recentSearches={recentSearches}
/>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{/* 顶部 Header */}
<Header />
<AnimatePresence mode="wait">
{sidebarOpen && <Sidebar navItems={navItems} />}
</AnimatePresence>
<main
className={`pt-16 min-h-screen transition-all duration-300 ${sidebarOpen ? 'ml-64' : 'ml-0'
}`}
>
<Outlet></Outlet>
{/* 主要内容区域 */}
<main className="min-h-screen bg-slate-50">
<div className="container mx-auto px-4 py-8">
<Outlet />
</div>
</main>
</div>
);
}
{/* 底部 Footer */}
<Footer />
</motion.div>
)
}

View File

@ -0,0 +1,43 @@
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import { useState, useCallback } from "react";
import debounce from "lodash/debounce";
interface SearchBarProps {
onSearch?: (query: string) => void;
}
export const SearchBar = ({ onSearch }: SearchBarProps) => {
const [query, setQuery] = useState("");
const debouncedSearch = useCallback(
debounce((value: string) => {
onSearch?.(value);
}, 300),
[onSearch]
);
return (
<div className="flex-1 max-w-xl mx-12">
<div className="group relative">
<input
type="search"
value={query}
onChange={(e) => {
setQuery(e.target.value);
debouncedSearch(e.target.value);
}}
placeholder="Search letters, announcements, policies..."
className="w-full rounded-lg border border-transparent bg-[#0B1A32]
px-4 py-2.5 pl-10 text-white placeholder-[#8EADD4]
transition-all duration-300 focus:border-[#8EADD4]
focus:outline-none focus:ring-1 focus:ring-[#8EADD4]"
aria-label="Search"
/>
<MagnifyingGlassIcon
className="absolute left-3 top-3 h-5 w-5 text-[#8EADD4]
transition-colors group-hover:text-white"
/>
</div>
</div>
);
};

View File

@ -0,0 +1,7 @@
export const NAV_ITEMS = [
{ to: "/write-letter", label: "Write Letter" },
{ to: "/search-letter", label: "Search Letters" },
{ to: "/public-letters", label: "Public Letters" },
{ to: "/announcements", label: "Announcements" },
{ to: "/guidelines", label: "Guidelines" },
] as const;

View File

@ -1,43 +0,0 @@
import { NavItem } from '@nice/client';
import {
HomeIcon,
BookOpenIcon,
UserGroupIcon,
Cog6ToothIcon,
BellIcon,
HeartIcon,
AcademicCapIcon,
UsersIcon,
PresentationChartBarIcon
} from '@heroicons/react/24/outline';
export const navItems: NavItem[] = [
{ icon: <HomeIcon className="w-6 h-6" />, label: '探索知识', path: '/' },
{ icon: <AcademicCapIcon className="w-6 h-6" />, label: '我的学习', path: '/courses/student' },
{ icon: <PresentationChartBarIcon className="w-6 h-6" />, label: '我的授课', path: '/courses/instructor' },
{ icon: <UsersIcon className="w-6 h-6" />, label: '学习社区', path: '/community' },
{ icon: <Cog6ToothIcon className="w-6 h-6" />, label: '应用设置', path: '/settings' },
];
export const notificationItems = [
{
icon: <BellIcon className="w-6 h-6 text-blue-500" />,
title: "New Course Available",
description: "Advanced TypeScript Programming is now available",
time: "2 hours ago",
isUnread: true,
},
{
icon: <HeartIcon className="w-6 h-6 text-red-500" />,
title: "Course Recommendation",
description: "Based on your interests: React Native Development",
time: "1 day ago",
isUnread: true,
},
{
icon: <AcademicCapIcon className="w-6 h-6 text-green-500" />,
title: "Certificate Ready",
description: "Your React Fundamentals certificate is ready to download",
time: "2 days ago",
isUnread: true,
},
];

View File

@ -20,6 +20,8 @@ import CourseContentForm from "../components/models/course/editor/form/CourseCon
import { CourseGoalForm } from "../components/models/course/editor/form/CourseGoalForm";
import CourseSettingForm from "../components/models/course/editor/form/CourseSettingForm";
import CourseEditorLayout from "../components/models/course/editor/layout/CourseEditorLayout";
import WriteLetterPage from "../app/main/letter/write/page";
import LetterListPage from "../app/main/letter/list/page";
interface CustomIndexRouteObject extends IndexRouteObject {
name?: string;
@ -29,7 +31,11 @@ interface CustomIndexRouteObject extends IndexRouteObject {
name?: string;
breadcrumb?: string;
}
export interface NavItem {
icon?: React.ReactNode;
label: string;
path: string;
}
export interface CustomNonIndexRouteObject extends NonIndexRouteObject {
name?: string;
children?: CustomRouteObject[];
@ -57,8 +63,16 @@ export const routes: CustomRouteObject[] = [
{
index: true,
element: <HomePage />,
},
{
path: "write-letter",
element: <WriteLetterPage></WriteLetterPage>,
},
{
path: "list",
element: <LetterListPage></LetterListPage>
}
],
},
{

View File

@ -1,5 +1 @@
export interface NavItem {
icon?: React.ReactNode;
label: string;
path: string;
}
export { }

View File

@ -12,7 +12,7 @@ importers:
dependencies:
'@nestjs/bullmq':
specifier: ^10.2.0
version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8)
version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8)
'@nestjs/common':
specifier: ^10.3.10
version: 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -33,7 +33,7 @@ importers:
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.15)(rxjs@7.8.1)
'@nestjs/schedule':
specifier: ^4.1.0
version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))
version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)
'@nestjs/websockets':
specifier: ^10.3.10
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-socket.io@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -142,13 +142,13 @@ importers:
devDependencies:
'@nestjs/cli':
specifier: ^10.0.0
version: 10.4.9(@swc/core@1.10.6)
version: 10.4.9(@swc/core@1.10.6(@swc/helpers@0.5.15))
'@nestjs/schematics':
specifier: ^10.0.0
version: 10.2.3(chokidar@3.6.0)(typescript@5.7.2)
'@nestjs/testing':
specifier: ^10.0.0
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15))
version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15)
'@types/exceljs':
specifier: ^1.3.0
version: 1.3.2
@ -196,7 +196,7 @@ importers:
version: 5.2.1(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.4.2)
jest:
specifier: ^29.5.0
version: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
version: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
prettier:
specifier: ^3.0.0
version: 3.4.2
@ -208,13 +208,13 @@ importers:
version: 6.3.4
ts-jest:
specifier: ^29.1.0
version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)))(typescript@5.7.2)
version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)))(typescript@5.7.2)
ts-loader:
specifier: ^9.4.3
version: 9.5.1(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6))
version: 9.5.1(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15)))
ts-node:
specifier: ^10.9.1
version: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
version: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0
@ -395,6 +395,9 @@ importers:
superjson:
specifier: ^2.2.1
version: 2.2.2
swiper:
specifier: ^11.2.1
version: 11.2.1
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
@ -440,7 +443,7 @@ importers:
version: 8.4.49
tailwindcss:
specifier: ^3.4.10
version: 3.4.17(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2))
version: 3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -501,7 +504,7 @@ importers:
version: 6.0.1
tsup:
specifier: ^8.3.5
version: 8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
version: 8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -535,10 +538,10 @@ importers:
version: 6.0.1
ts-node:
specifier: ^10.9.1
version: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
version: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
tsup:
specifier: ^8.3.5
version: 8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
version: 8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -604,10 +607,10 @@ importers:
version: 6.0.1
ts-node:
specifier: ^10.9.1
version: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
version: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
tsup:
specifier: ^8.3.5
version: 8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
version: 8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -659,10 +662,10 @@ importers:
version: 13.2.3
ts-node:
specifier: ^10.9.1
version: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
version: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
tsup:
specifier: ^8.3.5
version: 8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
version: 8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -717,10 +720,10 @@ importers:
version: 6.0.1
ts-node:
specifier: ^10.9.1
version: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
version: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
tsup:
specifier: ^8.3.5
version: 8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
version: 8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -738,10 +741,10 @@ importers:
version: 6.0.1
ts-node:
specifier: ^10.9.1
version: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
version: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
tsup:
specifier: ^8.3.5
version: 8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
version: 8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0)
typescript:
specifier: ^5.5.4
version: 5.7.2
@ -6425,6 +6428,10 @@ packages:
svg-parser@2.0.4:
resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
swiper@11.2.1:
resolution: {integrity: sha512-62G69+iQRIfUqTmJkWpZDcX891Ra8O9050ckt1/JI2H+0483g+gq0m7gINecDqMtDh2zt5dK+uzBRxGhGOOvQA==}
engines: {node: '>= 4.7.0'}
symbol-observable@4.0.0:
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
engines: {node: '>=0.10'}
@ -8327,7 +8334,7 @@ snapshots:
jest-util: 29.7.0
slash: 3.0.0
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))':
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))':
dependencies:
'@jest/console': 29.7.0
'@jest/reporters': 29.7.0
@ -8341,7 +8348,7 @@ snapshots:
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@ -8531,21 +8538,21 @@ snapshots:
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
optional: true
'@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
'@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)':
dependencies:
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
tslib: 2.8.1
'@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8)':
'@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8)':
dependencies:
'@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))
'@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
bullmq: 5.34.8
tslib: 2.8.1
'@nestjs/cli@10.4.9(@swc/core@1.10.6)':
'@nestjs/cli@10.4.9(@swc/core@1.10.6(@swc/helpers@0.5.15))':
dependencies:
'@angular-devkit/core': 17.3.11(chokidar@3.6.0)
'@angular-devkit/schematics': 17.3.11(chokidar@3.6.0)
@ -8555,7 +8562,7 @@ snapshots:
chokidar: 3.6.0
cli-table3: 0.6.5
commander: 4.1.1
fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6))
fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15)))
glob: 10.4.5
inquirer: 8.2.6
node-emoji: 1.11.0
@ -8564,7 +8571,7 @@ snapshots:
tsconfig-paths: 4.2.0
tsconfig-paths-webpack-plugin: 4.2.0
typescript: 5.7.2
webpack: 5.97.1(@swc/core@1.10.6)
webpack: 5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))
webpack-node-externals: 3.0.0
optionalDependencies:
'@swc/core': 1.10.6(@swc/helpers@0.5.15)
@ -8636,7 +8643,7 @@ snapshots:
- supports-color
- utf-8-validate
'@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
'@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)':
dependencies:
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -8654,7 +8661,7 @@ snapshots:
transitivePeerDependencies:
- chokidar
'@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15))':
'@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15)':
dependencies:
'@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)
@ -10687,13 +10694,13 @@ snapshots:
crc-32: 1.2.2
readable-stream: 3.6.2
create-jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)):
create-jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@ -11403,7 +11410,7 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6)):
fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))):
dependencies:
'@babel/code-frame': 7.26.2
chalk: 4.1.2
@ -11418,7 +11425,7 @@ snapshots:
semver: 7.6.3
tapable: 2.2.1
typescript: 5.7.2
webpack: 5.97.1(@swc/core@1.10.6)
webpack: 5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))
form-data@4.0.1:
dependencies:
@ -11864,16 +11871,16 @@ snapshots:
- babel-plugin-macros
- supports-color
jest-cli@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)):
jest-cli@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)):
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
create-jest: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
exit: 0.1.2
import-local: 3.2.0
jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@ -11883,7 +11890,7 @@ snapshots:
- supports-color
- ts-node
jest-config@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)):
jest-config@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)):
dependencies:
'@babel/core': 7.26.0
'@jest/test-sequencer': 29.7.0
@ -11909,7 +11916,7 @@ snapshots:
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 20.17.12
ts-node: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
ts-node: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@ -12135,12 +12142,12 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)):
jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)):
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
'@jest/types': 29.6.3
import-local: 3.2.0
jest-cli: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
jest-cli: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@ -12769,13 +12776,13 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.4.49
postcss-load-config@4.0.2(postcss@8.4.49)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)):
postcss-load-config@4.0.2(postcss@8.4.49)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)):
dependencies:
lilconfig: 3.1.3
yaml: 2.7.0
optionalDependencies:
postcss: 8.4.49
ts-node: 10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)
ts-node: 10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)
postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.4.49)(yaml@2.7.0):
dependencies:
@ -13838,6 +13845,8 @@ snapshots:
svg-parser@2.0.4: {}
swiper@11.2.1: {}
symbol-observable@4.0.0: {}
synckit@0.9.2:
@ -13849,7 +13858,7 @@ snapshots:
tailwind-merge@2.6.0: {}
tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)):
tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@ -13868,7 +13877,7 @@ snapshots:
postcss: 8.4.49
postcss-import: 15.1.0(postcss@8.4.49)
postcss-js: 4.0.1(postcss@8.4.49)
postcss-load-config: 4.0.2(postcss@8.4.49)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2))
postcss-load-config: 4.0.2(postcss@8.4.49)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
postcss-nested: 6.2.0(postcss@8.4.49)
postcss-selector-parser: 6.1.2
resolve: 1.22.10
@ -13886,14 +13895,14 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
terser-webpack-plugin@5.3.11(@swc/core@1.10.6)(webpack@5.97.1(@swc/core@1.10.6)):
terser-webpack-plugin@5.3.11(@swc/core@1.10.6(@swc/helpers@0.5.15))(webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
jest-worker: 27.5.1
schema-utils: 4.3.0
serialize-javascript: 6.0.2
terser: 5.37.0
webpack: 5.97.1(@swc/core@1.10.6)
webpack: 5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))
optionalDependencies:
'@swc/core': 1.10.6(@swc/helpers@0.5.15)
@ -13979,12 +13988,12 @@ snapshots:
ts-interface-checker@0.1.13: {}
ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2)))(typescript@5.7.2):
ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2)))(typescript@5.7.2):
dependencies:
bs-logger: 0.2.6
ejs: 3.1.10
fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2))
jest: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2))
jest-util: 29.7.0
json5: 2.2.3
lodash.memoize: 4.1.2
@ -13998,7 +14007,7 @@ snapshots:
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.26.0)
ts-loader@9.5.1(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6)):
ts-loader@9.5.1(typescript@5.7.2)(webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))):
dependencies:
chalk: 4.1.2
enhanced-resolve: 5.18.0
@ -14006,9 +14015,9 @@ snapshots:
semver: 7.6.3
source-map: 0.7.4
typescript: 5.7.2
webpack: 5.97.1(@swc/core@1.10.6)
webpack: 5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15))
ts-node@10.9.2(@swc/core@1.10.6)(@types/node@20.17.12)(typescript@5.7.2):
ts-node@10.9.2(@swc/core@1.10.6(@swc/helpers@0.5.15))(@types/node@20.17.12)(typescript@5.7.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
@ -14043,7 +14052,7 @@ snapshots:
tslib@2.8.1: {}
tsup@8.3.5(@swc/core@1.10.6)(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0):
tsup@8.3.5(@swc/core@1.10.6(@swc/helpers@0.5.15))(jiti@1.21.7)(postcss@8.4.49)(typescript@5.7.2)(yaml@2.7.0):
dependencies:
bundle-require: 5.1.0(esbuild@0.24.2)
cac: 6.7.14
@ -14236,7 +14245,7 @@ snapshots:
webpack-sources@3.2.3: {}
webpack@5.97.1(@swc/core@1.10.6):
webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15)):
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.6
@ -14258,7 +14267,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.11(@swc/core@1.10.6)(webpack@5.97.1(@swc/core@1.10.6))
terser-webpack-plugin: 5.3.11(@swc/core@1.10.6(@swc/helpers@0.5.15))(webpack@5.97.1(@swc/core@1.10.6(@swc/helpers@0.5.15)))
watchpack: 2.4.2
webpack-sources: 3.2.3
transitivePeerDependencies: