chore: release [ci skip]

This commit is contained in:
gitea-actions
2026-04-17 06:48:04 +00:00
parent 60b5233e95
commit 77f9a11132
22 changed files with 564 additions and 327 deletions
+91 -95
View File
@@ -5,7 +5,8 @@ description: Create modern, responsive frontend components and layouts using ato
# Atomic Design Frontend System with Tailwind CSS # Atomic Design Frontend System with Tailwind CSS
A comprehensive guide for building modern, responsive frontend applications using atomic design principles and Tailwind CSS. A comprehensive guide for building modern, responsive frontend applications
using atomic design principles and Tailwind CSS.
## Core Principles ## Core Principles
@@ -14,20 +15,25 @@ A comprehensive guide for building modern, responsive frontend applications usin
Atomic Design breaks UI into five distinct levels: Atomic Design breaks UI into five distinct levels:
#### **Atoms** #### **Atoms**
Smallest, indivisible UI elements that cannot be broken down without losing functionality.
Smallest, indivisible UI elements that cannot be broken down without losing
functionality.
- Buttons, input fields, labels, icons, text styles - Buttons, input fields, labels, icons, text styles
- Color variables, spacing units, typography scales - Color variables, spacing units, typography scales
- Simple, pure, reusable building blocks - Simple, pure, reusable building blocks
```jsx ```jsx
// Example: Button Atom // Example: Button Atom
<button className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"> <button className="rounded-lg bg-blue-600 px-4 py-2 text-white transition-colors hover:bg-blue-700">
Click me Click me
</button> </button>
``` ```
#### **Molecules** #### **Molecules**
Groups of atoms bonded together, forming simple functional units. Groups of atoms bonded together, forming simple functional units.
- Search bars (input + button + icon) - Search bars (input + button + icon)
- Form fields (label + input + error message) - Form fields (label + input + error message)
- Card headers (avatar + title + subtitle) - Card headers (avatar + title + subtitle)
@@ -35,18 +41,16 @@ Groups of atoms bonded together, forming simple functional units.
```jsx ```jsx
// Example: Search Molecule // Example: Search Molecule
<div className="flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg"> <div className="flex items-center gap-2 rounded-lg border border-gray-300 px-4 py-2">
<SearchIcon className="w-5 h-5 text-gray-500" /> <SearchIcon className="h-5 w-5 text-gray-500" />
<input <input type="text" placeholder="Search..." className="flex-1 outline-none" />
type="text"
placeholder="Search..."
className="flex-1 outline-none"
/>
</div> </div>
``` ```
#### **Organisms** #### **Organisms**
Complex functional units made of groups of molecules and/or atoms. Complex functional units made of groups of molecules and/or atoms.
- Header/Navigation bars - Header/Navigation bars
- Form sections (multiple form molecules) - Form sections (multiple form molecules)
- Card layouts with multiple sections - Card layouts with multiple sections
@@ -55,28 +59,33 @@ Complex functional units made of groups of molecules and/or atoms.
```jsx ```jsx
// Example: Product Card Organism // Example: Product Card Organism
<div className="border border-gray-200 rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow"> <div className="overflow-hidden rounded-lg border border-gray-200 shadow-sm transition-shadow hover:shadow-md">
<img src="image.jpg" className="w-full h-48 object-cover" /> <img src="image.jpg" className="h-48 w-full object-cover" />
<div className="p-4"> <div className="p-4">
<h3 className="font-semibold text-lg">Product Name</h3> <h3 className="text-lg font-semibold">Product Name</h3>
<p className="text-gray-600 text-sm mt-1">Description</p> <p className="mt-1 text-sm text-gray-600">Description</p>
<div className="flex justify-between items-center mt-4"> <div className="mt-4 flex items-center justify-between">
<span className="text-blue-600 font-bold">$99</span> <span className="font-bold text-blue-600">$99</span>
<button className="px-3 py-1 bg-blue-600 text-white rounded">Add</button> <button className="rounded bg-blue-600 px-3 py-1 text-white">Add</button>
</div> </div>
</div> </div>
</div> </div>
``` ```
#### **Templates** #### **Templates**
Page-level wireframes showing layout and component placement without final content.
Page-level wireframes showing layout and component placement without final
content.
- Single-column layouts - Single-column layouts
- Two-column layouts (sidebar + main) - Two-column layouts (sidebar + main)
- Grid-based layouts - Grid-based layouts
- Hero + content sections - Hero + content sections
#### **Pages** #### **Pages**
Specific instances of templates populated with real content and data. Specific instances of templates populated with real content and data.
- Homepage with actual products - Homepage with actual products
- User profile with real user data - User profile with real user data
- Dashboard with live metrics - Dashboard with live metrics
@@ -86,6 +95,7 @@ Specific instances of templates populated with real content and data.
## Design System Variables (Reusable Values) ## Design System Variables (Reusable Values)
### Color Palette ### Color Palette
```css ```css
/* Define in Tailwind config or use CSS variables */ /* Define in Tailwind config or use CSS variables */
Primary: #2563eb (blue-600) Primary: #2563eb (blue-600)
@@ -97,6 +107,7 @@ Neutral: #6b7280 (gray-500)
``` ```
### Typography Scale ### Typography Scale
```css ```css
H1: 32px (2rem) - font-bold H1: 32px (2rem) - font-bold
H2: 24px (1.5rem) - font-bold H2: 24px (1.5rem) - font-bold
@@ -107,6 +118,7 @@ Tiny: 12px (0.75rem) - font-normal
``` ```
### Spacing Scale ### Spacing Scale
```css ```css
xs: 4px (0.25rem) xs: 4px (0.25rem)
sm: 8px (0.5rem) sm: 8px (0.5rem)
@@ -117,6 +129,7 @@ xl: 32px (2rem)
``` ```
### Border Radius ### Border Radius
```css ```css
Subtle: 4px (rounded-sm) Subtle: 4px (rounded-sm)
Standard: 8px (rounded-lg) Standard: 8px (rounded-lg)
@@ -125,6 +138,7 @@ Full: 9999px (rounded-full)
``` ```
### Box Shadows ### Box Shadows
```css ```css
Subtle: 0 1px 2px rgba(0,0,0,0.05) Subtle: 0 1px 2px rgba(0,0,0,0.05)
Soft: 0 4px 6px rgba(0,0,0,0.07) Soft: 0 4px 6px rgba(0,0,0,0.07)
@@ -137,6 +151,7 @@ Strong: 0 20px 25px rgba(0,0,0,0.15)
## Responsive Design Strategy ## Responsive Design Strategy
### Breakpoints (Tailwind Default) ### Breakpoints (Tailwind Default)
``` ```
Mobile: < 640px (sm) Mobile: < 640px (sm)
Tablet: 640px (md, lg) Tablet: 640px (md, lg)
@@ -144,15 +159,17 @@ Desktop: 1024px+ (xl, 2xl)
``` ```
### Mobile-First Approach ### Mobile-First Approach
1. **Start with mobile styles** (default, no prefix) 1. **Start with mobile styles** (default, no prefix)
2. **Layer tablet styles** (md: prefix) 2. **Layer tablet styles** (md: prefix)
3. **Layer desktop styles** (lg:, xl: prefix) 3. **Layer desktop styles** (lg:, xl: prefix)
### Example: Responsive Layout ### Example: Responsive Layout
```jsx ```jsx
// Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns // Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{items.map(item => ( {items.map((item) => (
<Card key={item.id} {...item} /> <Card key={item.id} {...item} />
))} ))}
</div> </div>
@@ -161,39 +178,42 @@ Desktop: 1024px+ (xl, 2xl)
### Common Responsive Patterns ### Common Responsive Patterns
**Responsive Typography** **Responsive Typography**
```jsx ```jsx
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold"> <h1 className="text-2xl font-bold md:text-3xl lg:text-4xl">
Responsive Heading Responsive Heading
</h1> </h1>
``` ```
**Responsive Padding/Margins** **Responsive Padding/Margins**
```jsx ```jsx
<div className="p-4 md:p-6 lg:p-8"> <div className="p-4 md:p-6 lg:p-8">Content with responsive spacing</div>
Content with responsive spacing
</div>
``` ```
**Responsive Grid** **Responsive Grid**
```jsx ```jsx
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-6"> <div className="grid grid-cols-2 gap-4 md:grid-cols-3 md:gap-6 lg:grid-cols-4">
{/* Grid items */} {/* Grid items */}
</div> </div>
``` ```
**Responsive Flexbox** **Responsive Flexbox**
```jsx ```jsx
<div className="flex flex-col md:flex-row gap-4"> <div className="flex flex-col gap-4 md:flex-row">
<aside className="w-full md:w-64">Sidebar</aside> <aside className="w-full md:w-64">Sidebar</aside>
<main className="flex-1">Main content</main> <main className="flex-1">Main content</main>
</div> </div>
``` ```
**Responsive Images** **Responsive Images**
```jsx ```jsx
<img <img
src="image.jpg" src="image.jpg"
className="w-full h-auto object-cover" className="h-auto w-full object-cover"
alt="Responsive image" alt="Responsive image"
/> />
``` ```
@@ -203,6 +223,7 @@ Desktop: 1024px+ (xl, 2xl)
## Modern Design Patterns ## Modern Design Patterns
### 1. Consistency & Visual Hierarchy ### 1. Consistency & Visual Hierarchy
- **Use consistent spacing**: Apply spacing scale uniformly - **Use consistent spacing**: Apply spacing scale uniformly
- **Establish clear hierarchy**: Size, weight, color for emphasis - **Establish clear hierarchy**: Size, weight, color for emphasis
- **Group related content**: Use whitespace to separate sections - **Group related content**: Use whitespace to separate sections
@@ -211,16 +232,15 @@ Desktop: 1024px+ (xl, 2xl)
```jsx ```jsx
<section className="space-y-6"> <section className="space-y-6">
<div> <div>
<h2 className="text-2xl font-bold mb-2">Section Title</h2> <h2 className="mb-2 text-2xl font-bold">Section Title</h2>
<p className="text-gray-600">Description text</p> <p className="text-gray-600">Description text</p>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">{/* Related items with consistent spacing */}</div>
{/* Related items with consistent spacing */}
</div>
</section> </section>
``` ```
### 2. Interactive Feedback ### 2. Interactive Feedback
- **Hover states**: Subtle color/shadow changes - **Hover states**: Subtle color/shadow changes
- **Active states**: Indicate current selection - **Active states**: Indicate current selection
- **Focus states**: Keyboard navigation support - **Focus states**: Keyboard navigation support
@@ -228,35 +248,26 @@ Desktop: 1024px+ (xl, 2xl)
- **Transitions**: Smooth animations (200-300ms) - **Transitions**: Smooth animations (200-300ms)
```jsx ```jsx
<button className=" <button className="rounded-lg bg-blue-600 px-4 py-2 text-white transition-all duration-200 hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:outline-none active:scale-95 disabled:cursor-not-allowed disabled:opacity-50">
px-4 py-2 bg-blue-600 text-white rounded-lg
hover:bg-blue-700
active:scale-95
focus:outline-none focus:ring-2 focus:ring-blue-500
transition-all duration-200
disabled:opacity-50 disabled:cursor-not-allowed
">
Actionable Button Actionable Button
</button> </button>
``` ```
### 3. Depth & Layering ### 3. Depth & Layering
- **Subtle shadows**: Create depth without heaviness - **Subtle shadows**: Create depth without heaviness
- **Elevation levels**: Consistent shadow progression - **Elevation levels**: Consistent shadow progression
- **Overlays**: Semi-transparent backgrounds for modals - **Overlays**: Semi-transparent backgrounds for modals
- **Z-index strategy**: Clear layering hierarchy - **Z-index strategy**: Clear layering hierarchy
```jsx ```jsx
<div className=" <div className="rounded-lg border border-gray-200 shadow-sm transition-shadow duration-300 hover:shadow-md">
border border-gray-200 rounded-lg
shadow-sm hover:shadow-md
transition-shadow duration-300
">
Card content Card content
</div> </div>
``` ```
### 4. Color Usage ### 4. Color Usage
- **Primary action**: Most frequent call-to-action - **Primary action**: Most frequent call-to-action
- **Secondary action**: Alternative actions - **Secondary action**: Alternative actions
- **Semantic colors**: Status indicators (success, warning, danger) - **Semantic colors**: Status indicators (success, warning, danger)
@@ -264,16 +275,15 @@ Desktop: 1024px+ (xl, 2xl)
- **Limited palette**: 3-5 colors maximum in most designs - **Limited palette**: 3-5 colors maximum in most designs
### 5. Whitespace & Breathing Room ### 5. Whitespace & Breathing Room
- Don't crowd elements - Don't crowd elements
- Use consistent gap values (gap-4, gap-6, gap-8) - Use consistent gap values (gap-4, gap-6, gap-8)
- Separate sections with vertical rhythm - Separate sections with vertical rhythm
- Generous padding in cards and containers - Generous padding in cards and containers
```jsx ```jsx
<div className="max-w-4xl mx-auto px-4 md:px-6 py-8 md:py-12"> <div className="mx-auto max-w-4xl px-4 py-8 md:px-6 md:py-12">
<div className="space-y-8"> <div className="space-y-8">{/* Sections with good breathing room */}</div>
{/* Sections with good breathing room */}
</div>
</div> </div>
``` ```
@@ -282,6 +292,7 @@ Desktop: 1024px+ (xl, 2xl)
## Tailwind CSS Best Practices ## Tailwind CSS Best Practices
### 1. Use Utility Classes Effectively ### 1. Use Utility Classes Effectively
```jsx ```jsx
// Good: Semantic, reusable, organized // Good: Semantic, reusable, organized
<button className=" <button className="
@@ -300,14 +311,15 @@ Desktop: 1024px+ (xl, 2xl)
``` ```
### 2. Extract Reusable Components ### 2. Extract Reusable Components
```jsx ```jsx
// Create a Button component to avoid repetition // Create a Button component to avoid repetition
const Button = ({ children, variant = 'primary', ...props }) => { const Button = ({ children, variant = "primary", ...props }) => {
const baseStyles = 'px-4 py-2 rounded-lg font-medium transition-colors'; const baseStyles = "px-4 py-2 rounded-lg font-medium transition-colors";
const variants = { const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700', primary: "bg-blue-600 text-white hover:bg-blue-700",
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300', secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
danger: 'bg-red-600 text-white hover:bg-red-700', danger: "bg-red-600 text-white hover:bg-red-700",
}; };
return ( return (
@@ -319,39 +331,25 @@ const Button = ({ children, variant = 'primary', ...props }) => {
``` ```
### 3. Organize Utility Classes ### 3. Organize Utility Classes
```jsx ```jsx
// Organize by category: layout → sizing → colors → effects → responsive // Organize by category: layout → sizing → colors → effects → responsive
<div className=" <div className="/* Layout */ /* Sizing */ /* Spacing */ /* Colors & text */ /* Borders & shadows */ /* Effects */ /* Responsive */ my-8 flex w-full max-w-2xl flex-col gap-4 rounded-lg border border-gray-200 bg-white p-6 text-gray-900 shadow-sm transition-shadow hover:shadow-md md:flex-row lg:gap-6"></div>
/* Layout */
flex flex-col gap-4
/* Sizing */
w-full max-w-2xl
/* Spacing */
p-6 my-8
/* Colors & text */
bg-white text-gray-900
/* Borders & shadows */
border border-gray-200 rounded-lg shadow-sm
/* Effects */
hover:shadow-md transition-shadow
/* Responsive */
md:flex-row lg:gap-6
">
</div>
``` ```
### 4. Use Tailwind Config for Consistency ### 4. Use Tailwind Config for Consistency
```js ```js
// tailwind.config.js // tailwind.config.js
module.exports = { module.exports = {
theme: { theme: {
extend: { extend: {
colors: { colors: {
primary: '#2563eb', primary: "#2563eb",
secondary: '#7c3aed', secondary: "#7c3aed",
}, },
spacing: { spacing: {
gutter: '1rem', gutter: "1rem",
}, },
}, },
}, },
@@ -376,17 +374,11 @@ Use this template when building atomic components:
* - prop2: type - description * - prop2: type - description
*/ */
export const ComponentName = ({ prop1, prop2, className = '' }) => { export const ComponentName = ({ prop1, prop2, className = "" }) => {
return ( return (
<div className={` <div
/* Base styles */ className={`/* Base styles */ /* Responsive */ /* Custom className */ flex flex-col gap-4 rounded-lg border border-gray-200 bg-white p-6 md:flex-row lg:p-8 ${className} `}
flex flex-col gap-4 p-6 >
bg-white border border-gray-200 rounded-lg
/* Responsive */
md:flex-row lg:p-8
/* Custom className */
${className}
`}>
{/* Component content */} {/* Component content */}
</div> </div>
); );
@@ -398,42 +390,46 @@ export const ComponentName = ({ prop1, prop2, className = '' }) => {
## Layout Patterns ## Layout Patterns
### Container + Padding ### Container + Padding
```jsx ```jsx
<div className="max-w-6xl mx-auto px-4 md:px-6 lg:px-8"> <div className="mx-auto max-w-6xl px-4 md:px-6 lg:px-8">
{/* Content constrained to max width with responsive padding */} {/* Content constrained to max width with responsive padding */}
</div> </div>
``` ```
### Two-Column Sidebar Layout ### Two-Column Sidebar Layout
```jsx ```jsx
<div className="flex flex-col lg:flex-row gap-6"> <div className="flex flex-col gap-6 lg:flex-row">
<aside className="w-full lg:w-64 flex-shrink-0"> <aside className="w-full flex-shrink-0 lg:w-64">
{/* Sidebar: full width on mobile, fixed on desktop */} {/* Sidebar: full width on mobile, fixed on desktop */}
</aside> </aside>
<main className="flex-1 min-w-0"> <main className="min-w-0 flex-1">
{/* Main content: takes remaining space */} {/* Main content: takes remaining space */}
</main> </main>
</div> </div>
``` ```
### Responsive Grid ### Responsive Grid
```jsx ```jsx
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{/* Items automatically stack on mobile, 2 columns on tablet, 3 on desktop */} {/* Items automatically stack on mobile, 2 columns on tablet, 3 on desktop */}
</div> </div>
``` ```
### Hero Section ### Hero Section
```jsx ```jsx
<section className="relative bg-gradient-to-r from-blue-600 to-violet-600 text-white overflow-hidden py-12 md:py-20 lg:py-32"> <section className="relative overflow-hidden bg-gradient-to-r from-blue-600 to-violet-600 py-12 text-white md:py-20 lg:py-32">
<div className="max-w-6xl mx-auto px-4 md:px-6 relative z-10"> <div className="relative z-10 mx-auto max-w-6xl px-4 md:px-6">
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold mb-4"> <h1 className="mb-4 text-4xl font-bold md:text-5xl lg:text-6xl">
Hero Title Hero Title
</h1> </h1>
<p className="text-lg md:text-xl opacity-90 mb-8 max-w-2xl"> <p className="mb-8 max-w-2xl text-lg opacity-90 md:text-xl">
Hero subtitle or description Hero subtitle or description
</p> </p>
<button className="px-8 py-3 bg-white text-blue-600 rounded-lg font-semibold hover:bg-gray-100 transition-colors"> <button className="rounded-lg bg-white px-8 py-3 font-semibold text-blue-600 transition-colors hover:bg-gray-100">
Call to Action Call to Action
</button> </button>
</div> </div>
@@ -453,7 +449,7 @@ export const ComponentName = ({ prop1, prop2, className = '' }) => {
```jsx ```jsx
<button <button
className="px-4 py-3 min-h-[44px] focus:outline-none focus:ring-2 focus:ring-offset-2" className="min-h-[44px] px-4 py-3 focus:ring-2 focus:ring-offset-2 focus:outline-none"
aria-label="Close modal" aria-label="Close modal"
> >
@@ -475,6 +471,7 @@ export const ComponentName = ({ prop1, prop2, className = '' }) => {
## Quick Reference Checklist ## Quick Reference Checklist
When building a component, ensure: When building a component, ensure:
- [ ] Follows atomic design hierarchy (Atom/Molecule/Organism) - [ ] Follows atomic design hierarchy (Atom/Molecule/Organism)
- [ ] Uses design system variables (colors, spacing, typography) - [ ] Uses design system variables (colors, spacing, typography)
- [ ] Responsive across mobile, tablet, desktop - [ ] Responsive across mobile, tablet, desktop
@@ -484,4 +481,3 @@ When building a component, ensure:
- [ ] Accessible (contrast, focus, labels) - [ ] Accessible (contrast, focus, labels)
- [ ] Mobile-first approach - [ ] Mobile-first approach
- [ ] No hardcoded values (use Tailwind config) - [ ] No hardcoded values (use Tailwind config)
+34 -10
View File
@@ -1,6 +1,11 @@
--- ---
name: prompt-optimizer name: prompt-optimizer
description: Help users rewrite and improve AI/LLM prompts by adding specificity, context, and constraints. Trigger this skill whenever users ask to improve, rewrite, optimize, or refine prompts for AI models. Focus on making prompts clearer, more specific, and more likely to produce better AI results. Present suggestions interactively so users can choose which improvements to apply. description:
Help users rewrite and improve AI/LLM prompts by adding specificity, context,
and constraints. Trigger this skill whenever users ask to improve, rewrite,
optimize, or refine prompts for AI models. Focus on making prompts clearer,
more specific, and more likely to produce better AI results. Present
suggestions interactively so users can choose which improvements to apply.
--- ---
# Prompt Optimizer # Prompt Optimizer
@@ -9,32 +14,42 @@ A beginner-friendly skill for improving AI/LLM prompts to get better results.
## What This Skill Does ## What This Skill Does
This skill helps you rewrite prompts to work better with AI models like Claude. Instead of just giving you a rewritten prompt, it shows you specific improvement suggestions that you can choose to apply or skip. This skill helps you rewrite prompts to work better with AI models like Claude.
Instead of just giving you a rewritten prompt, it shows you specific improvement
suggestions that you can choose to apply or skip.
## Key Improvements ## Key Improvements
When optimizing a prompt, focus on three main areas: When optimizing a prompt, focus on three main areas:
### 1. **Specificity** — Making the Request Clear ### 1. **Specificity** — Making the Request Clear
Good prompts are specific about what you want. Vague prompts get vague results. Good prompts are specific about what you want. Vague prompts get vague results.
**Example improvements:** **Example improvements:**
- Add details about format: "Give me a bullet list of 5 items" instead of "tell me about X"
- Add details about format: "Give me a bullet list of 5 items" instead of "tell
me about X"
- Be clear about length: "Write 200 words" instead of "Write something short" - Be clear about length: "Write 200 words" instead of "Write something short"
- Define who the audience is: "Explain this for a 10-year-old" or "Use technical language" - Define who the audience is: "Explain this for a 10-year-old" or "Use technical
language"
### 2. **Context** — Giving the AI Background Information ### 2. **Context** — Giving the AI Background Information
More context helps the AI make better decisions. More context helps the AI make better decisions.
**Example improvements:** **Example improvements:**
- Explain the goal: "I'm writing a resume, so focus on professional language" - Explain the goal: "I'm writing a resume, so focus on professional language"
- Share constraints: "We only have $500 budget" or "It needs to work on mobile" - Share constraints: "We only have $500 budget" or "It needs to work on mobile"
- Provide background: "I already know Python but not JavaScript" - Provide background: "I already know Python but not JavaScript"
### 3. **Constraints** — Setting Boundaries ### 3. **Constraints** — Setting Boundaries
Constraints prevent unwanted outputs. Constraints prevent unwanted outputs.
**Example improvements:** **Example improvements:**
- Set length limits: "Keep it under 100 words" - Set length limits: "Keep it under 100 words"
- Specify format: "Use JSON format" or "Write as a numbered list" - Specify format: "Use JSON format" or "Write as a numbered list"
- Define tone: "Be casual and friendly, not formal" - Define tone: "Be casual and friendly, not formal"
@@ -45,16 +60,20 @@ Constraints prevent unwanted outputs.
1. **Share your prompt** — Give me the original prompt you want to improve 1. **Share your prompt** — Give me the original prompt you want to improve
2. **Review suggestions** — I'll show you specific improvements in each area 2. **Review suggestions** — I'll show you specific improvements in each area
3. **Choose what you like** — Pick which suggestions to apply 3. **Choose what you like** — Pick which suggestions to apply
4. **Get the final version** — I'll rewrite your prompt with your chosen improvements 4. **Get the final version** — I'll rewrite your prompt with your chosen
improvements
## Interactive Selection Process ## Interactive Selection Process
When you use this skill, you'll see: When you use this skill, you'll see:
- **Original prompt** — Your starting point - **Original prompt** — Your starting point
- **Improvement suggestions** — Specific changes grouped by category (Specificity, Context, Constraints) - **Improvement suggestions** — Specific changes grouped by category
- **Preview examples** — What each change would look like with the improvement applied (Specificity, Context, Constraints)
- **Your choices** — You pick which suggestions help most (you can apply all, some, or none) - **Preview examples** — What each change would look like with the improvement
applied
- **Your choices** — You pick which suggestions help most (you can apply all,
some, or none)
Then you get a rewritten prompt combining all your choices. Then you get a rewritten prompt combining all your choices.
@@ -63,14 +82,18 @@ Then you get a rewritten prompt combining all your choices.
**Original:** "Write me a blog post" **Original:** "Write me a blog post"
**Suggestions I might offer:** **Suggestions I might offer:**
- **Specificity**: Add a topic (e.g., "about sustainable living") - **Specificity**: Add a topic (e.g., "about sustainable living")
- **Context**: Explain your goal (e.g., "to build authority on my website") - **Context**: Explain your goal (e.g., "to build authority on my website")
- **Constraints**: Set a word count (e.g., "800-1000 words") - **Constraints**: Set a word count (e.g., "800-1000 words")
**Your choice:** "I want all three — add topic, goal, and word count" **Your choice:** "I want all three — add topic, goal, and word count"
**Final rewritten prompt:** **Final rewritten prompt:** "Write an 800-1000 word blog post about sustainable
"Write an 800-1000 word blog post about sustainable living for my website. The goal is to establish my authority on eco-friendly practices. Target an audience of people interested in reducing their carbon footprint. Include 3-4 practical tips they can implement immediately, and end with a call-to-action encouraging them to sign up for my newsletter." living for my website. The goal is to establish my authority on eco-friendly
practices. Target an audience of people interested in reducing their carbon
footprint. Include 3-4 practical tips they can implement immediately, and end
with a call-to-action encouraging them to sign up for my newsletter."
## Tips for Best Results ## Tips for Best Results
@@ -82,6 +105,7 @@ Then you get a rewritten prompt combining all your choices.
## What Makes a Good Prompt ## What Makes a Good Prompt
A prompt becomes "good" when: A prompt becomes "good" when:
- The AI understands exactly what you want ✓ - The AI understands exactly what you want ✓
- You've given enough context to explain why ✓ - You've given enough context to explain why ✓
- You've set boundaries to prevent bad outputs ✓ - You've set boundaries to prevent bad outputs ✓
+57 -29
View File
@@ -1,6 +1,13 @@
--- ---
name: ui-ux-testing name: ui-ux-testing
description: Automated visual regression testing and UI/UX analysis for web applications. Use this skill whenever developers mention "test this UI", "visual regression", "test the UI", "check this interface", "UI testing", or want to create automated tests for web pages. Analyzes URLs and generates comprehensive test strategies including Playwright/Cypress test scripts, manual testing checklists, visual regression detection, and detailed reports with findings and recommendations. description:
Automated visual regression testing and UI/UX analysis for web applications.
Use this skill whenever developers mention "test this UI", "visual
regression", "test the UI", "check this interface", "UI testing", or want to
create automated tests for web pages. Analyzes URLs and generates
comprehensive test strategies including Playwright/Cypress test scripts,
manual testing checklists, visual regression detection, and detailed reports
with findings and recommendations.
compatibility: compatibility:
tools: Claude in Chrome browser automation tools: Claude in Chrome browser automation
frameworks: Playwright, Cypress, Selenium frameworks: Playwright, Cypress, Selenium
@@ -8,21 +15,28 @@ compatibility:
# UI/UX Testing Skill # UI/UX Testing Skill
This skill helps developers create automated visual regression tests and comprehensive UI/UX testing strategies for web applications. This skill helps developers create automated visual regression tests and
comprehensive UI/UX testing strategies for web applications.
## Overview ## Overview
When a developer asks you to test a UI or create visual regression tests, this skill guides you through: When a developer asks you to test a UI or create visual regression tests, this
skill guides you through:
1. **Analyzing the target URL** - Inspect the web page structure and components 1. **Analyzing the target URL** - Inspect the web page structure and components
2. **Generating test strategies** - Create both automated and manual testing approaches 2. **Generating test strategies** - Create both automated and manual testing
3. **Writing test code** - Generate Playwright/Cypress test scripts or Selenium code approaches
4. **Creating test checklists** - Manual testing steps for visual regression and UX flows 3. **Writing test code** - Generate Playwright/Cypress test scripts or Selenium
5. **Generating reports** - Detailed findings, issues, and recommendations in markdown/HTML code
4. **Creating test checklists** - Manual testing steps for visual regression and
UX flows
5. **Generating reports** - Detailed findings, issues, and recommendations in
markdown/HTML
## When to Trigger ## When to Trigger
Trigger this skill when the user: Trigger this skill when the user:
- Provides a URL and asks to "test this UI" - Provides a URL and asks to "test this UI"
- Requests "visual regression testing" for a web page - Requests "visual regression testing" for a web page
- Wants to "check accessibility" or test a component - Wants to "check accessibility" or test a component
@@ -35,6 +49,7 @@ Trigger this skill when the user:
### Step 1: Inspect the Target URL ### Step 1: Inspect the Target URL
Use Claude in Chrome to: Use Claude in Chrome to:
- Navigate to the provided URL - Navigate to the provided URL
- Take screenshots of different viewport sizes (desktop, tablet, mobile) - Take screenshots of different viewport sizes (desktop, tablet, mobile)
- Inspect the DOM structure using `read_page` tool - Inspect the DOM structure using `read_page` tool
@@ -44,6 +59,7 @@ Use Claude in Chrome to:
### Step 2: Create a Test Strategy ### Step 2: Create a Test Strategy
Based on your inspection, identify: Based on your inspection, identify:
- **Visual elements** to regression test (buttons, forms, headers, layouts) - **Visual elements** to regression test (buttons, forms, headers, layouts)
- **Interactive flows** to test (hover states, click handlers, form submission) - **Interactive flows** to test (hover states, click handlers, form submission)
- **Responsive breakpoints** to validate (mobile, tablet, desktop) - **Responsive breakpoints** to validate (mobile, tablet, desktop)
@@ -55,48 +71,51 @@ Based on your inspection, identify:
**For Automated Testing (Choose one or more):** **For Automated Testing (Choose one or more):**
#### Playwright (Recommended) #### Playwright (Recommended)
```javascript ```javascript
// Example structure // Example structure
import { test, expect } from '@playwright/test'; import { expect, test } from "@playwright/test";
test('visual regression - homepage', async ({ page }) => { test("visual regression - homepage", async ({ page }) => {
await page.goto('https://example.com'); await page.goto("https://example.com");
// Capture baseline screenshot // Capture baseline screenshot
await expect(page).toHaveScreenshot('homepage.png'); await expect(page).toHaveScreenshot("homepage.png");
// Test interactive elements // Test interactive elements
await page.hover('button.primary'); await page.hover("button.primary");
await expect(page).toHaveScreenshot('button-hover.png'); await expect(page).toHaveScreenshot("button-hover.png");
}); });
test('responsive layout - mobile', async ({ page }) => { test("responsive layout - mobile", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 812 }); await page.setViewportSize({ width: 375, height: 812 });
await page.goto('https://example.com'); await page.goto("https://example.com");
await expect(page).toHaveScreenshot('mobile-layout.png'); await expect(page).toHaveScreenshot("mobile-layout.png");
}); });
``` ```
#### Cypress #### Cypress
```javascript ```javascript
describe('Visual Regression Tests', () => { describe("Visual Regression Tests", () => {
beforeEach(() => { beforeEach(() => {
cy.visit('https://example.com'); cy.visit("https://example.com");
}); });
it('captures baseline screenshot', () => { it("captures baseline screenshot", () => {
cy.screenshot('homepage'); cy.screenshot("homepage");
cy.get('[data-testid="header"]').should('be.visible'); cy.get('[data-testid="header"]').should("be.visible");
}); });
it('tests button hover state', () => { it("tests button hover state", () => {
cy.get('button.primary').trigger('mouseenter'); cy.get("button.primary").trigger("mouseenter");
cy.screenshot('button-hover-state'); cy.screenshot("button-hover-state");
}); });
}); });
``` ```
#### Selenium #### Selenium
```python ```python
from selenium import webdriver from selenium import webdriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@@ -156,6 +175,7 @@ Create an HTML/Markdown report with:
# UI/UX Testing Report # UI/UX Testing Report
## Executive Summary ## Executive Summary
- URL tested: [URL] - URL tested: [URL]
- Viewports tested: Desktop (1920x1080), Tablet (768x1024), Mobile (375x667) - Viewports tested: Desktop (1920x1080), Tablet (768x1024), Mobile (375x667)
- Testing date: [Date] - Testing date: [Date]
@@ -164,6 +184,7 @@ Create an HTML/Markdown report with:
## Issues Found ## Issues Found
### Critical (Breaks functionality) ### Critical (Breaks functionality)
1. **Issue Title** 1. **Issue Title**
- Severity: Critical - Severity: Critical
- Location: [Element/Component] - Location: [Element/Component]
@@ -173,12 +194,14 @@ Create an HTML/Markdown report with:
- Screenshot: [If applicable] - Screenshot: [If applicable]
### Major (Significant visual/UX impact) ### Major (Significant visual/UX impact)
1. **Issue Title** 1. **Issue Title**
- Severity: Major - Severity: Major
- Location: [Element/Component] - Location: [Element/Component]
- Impact: [User impact] - Impact: [User impact]
### Minor (Polish/optimization) ### Minor (Polish/optimization)
1. **Issue Title** 1. **Issue Title**
- Severity: Minor - Severity: Minor
- Location: [Element/Component] - Location: [Element/Component]
@@ -187,21 +210,24 @@ Create an HTML/Markdown report with:
## Visual Regression Analysis ## Visual Regression Analysis
### Desktop (1920x1080) ### Desktop (1920x1080)
- [List observations] - [List observations]
- [List changes from baseline if available] - [List changes from baseline if available]
### Tablet (768x1024) ### Tablet (768x1024)
- [List observations] - [List observations]
- [Responsive issues found] - [Responsive issues found]
### Mobile (375x667) ### Mobile (375x667)
- [List observations] - [List observations]
- [Mobile-specific issues] - [Mobile-specific issues]
## Accessibility Assessment ## Accessibility Assessment
| Element | Issue | WCAG Level | Recommendation | | Element | Issue | WCAG Level | Recommendation |
|---------|-------|-----------|-----------------| | --------- | ------- | ---------- | -------------- |
| [Element] | [Issue] | [AA/AAA] | [Fix] | | [Element] | [Issue] | [AA/AAA] | [Fix] |
## Recommendations ## Recommendations
@@ -223,6 +249,7 @@ Create an HTML/Markdown report with:
- Regression risk: [High/Medium/Low] - Regression risk: [High/Medium/Low]
--- ---
Generated using UI/UX Testing Skill Generated using UI/UX Testing Skill
``` ```
@@ -237,12 +264,14 @@ Based on what the developer needs, generate:
## Best Practices ## Best Practices
- **Multiple viewports:** Always test at least mobile (375px), tablet (768px), and desktop (1920px) - **Multiple viewports:** Always test at least mobile (375px), tablet (768px),
and desktop (1920px)
- **Visual baselines:** Save baseline screenshots before making changes - **Visual baselines:** Save baseline screenshots before making changes
- **Critical paths:** Prioritize testing main user workflows first - **Critical paths:** Prioritize testing main user workflows first
- **Accessibility first:** Include WCAG AA compliance checks - **Accessibility first:** Include WCAG AA compliance checks
- **Clear assertions:** Make test assertions explicit and meaningful - **Clear assertions:** Make test assertions explicit and meaningful
- **Maintainability:** Use data attributes (data-testid) for reliable element selection - **Maintainability:** Use data attributes (data-testid) for reliable element
selection
## Example: Complete Testing Session ## Example: Complete Testing Session
@@ -270,5 +299,4 @@ Based on what the developer needs, generate:
--- ---
**Last Updated:** 2024 **Last Updated:** 2024 **Skill Version:** 1.0
**Skill Version:** 1.0
@@ -1,6 +1,14 @@
--- ---
name: website-creation-automation name: website-creation-automation
description: Automate end-to-end website creation with AI-powered optimization, design, and testing. Trigger this skill whenever the user wants to create a new website, build a landing page, design a web application, or automatically generate a complete website from a description. This skill takes a user's website idea or prompt, optimizes it for clarity, designs a modern responsive website, and automatically tests it for quality issues. Use this skill for any "create a website" or "build a web application" request, including landing pages, portfolio sites, e-commerce pages, dashboards, or any web-based project. description:
Automate end-to-end website creation with AI-powered optimization, design, and
testing. Trigger this skill whenever the user wants to create a new website,
build a landing page, design a web application, or automatically generate a
complete website from a description. This skill takes a user's website idea or
prompt, optimizes it for clarity, designs a modern responsive website, and
automatically tests it for quality issues. Use this skill for any "create a
website" or "build a web application" request, including landing pages,
portfolio sites, e-commerce pages, dashboards, or any web-based project.
compatibility: compatibility:
models: models:
- claude-sonnet-4-20250514 (design and testing) - claude-sonnet-4-20250514 (design and testing)
@@ -13,30 +21,38 @@ compatibility:
# Website Creation Automation Skill # Website Creation Automation Skill
An intelligent, end-to-end automation workflow for creating complete, tested websites from simple prompts. An intelligent, end-to-end automation workflow for creating complete, tested
websites from simple prompts.
## What This Skill Does ## What This Skill Does
This skill orchestrates a complete website creation pipeline: This skill orchestrates a complete website creation pipeline:
1. **Prompt Optimization** — Takes your website description and optimizes it into a detailed, structured prompt 1. **Prompt Optimization** — Takes your website description and optimizes it
2. **Website Design** — Uses the optimized prompt to design and build a modern, responsive website into a detailed, structured prompt
3. **Automated Testing**Tests the generated website for visual, interactive, and responsive issues 2. **Website Design**Uses the optimized prompt to design and build a modern,
responsive website
3. **Automated Testing** — Tests the generated website for visual, interactive,
and responsive issues
4. **Auto-Fix** — Detects and fixes any issues found during testing 4. **Auto-Fix** — Detects and fixes any issues found during testing
The entire workflow is automated, so you just provide a simple description of what you want, and the skill handles the rest. The entire workflow is automated, so you just provide a simple description of
what you want, and the skill handles the rest.
## Core Workflow ## Core Workflow
### Step 1: Optimize Your Website Prompt ### Step 1: Optimize Your Website Prompt
Your initial description (e.g., "Create a portfolio website for a freelance designer") is passed to the **prompt-optimizer** skill, which: Your initial description (e.g., "Create a portfolio website for a freelance
designer") is passed to the **prompt-optimizer** skill, which:
- Adds specific details about layout, features, and target audience - Adds specific details about layout, features, and target audience
- Clarifies design preferences and functionality requirements - Clarifies design preferences and functionality requirements
- Structures the request to guide high-quality website generation - Structures the request to guide high-quality website generation
- Returns a detailed, optimized prompt ready for design - Returns a detailed, optimized prompt ready for design
**Example transformation:** **Example transformation:**
``` ```
Input: "Create a portfolio website for a freelance designer" Input: "Create a portfolio website for a freelance designer"
@@ -51,6 +67,7 @@ and white space. Include smooth scroll animations."
### Step 2: Design the Website ### Step 2: Design the Website
The **frontend-atomic-design** skill uses the optimized prompt to: The **frontend-atomic-design** skill uses the optimized prompt to:
- Break down the website into atomic components (atoms, molecules, organisms) - Break down the website into atomic components (atoms, molecules, organisms)
- Create a responsive layout that works on desktop, tablet, and mobile - Create a responsive layout that works on desktop, tablet, and mobile
- Apply modern design patterns using Tailwind CSS - Apply modern design patterns using Tailwind CSS
@@ -58,6 +75,7 @@ The **frontend-atomic-design** skill uses the optimized prompt to:
- Generate a complete, production-ready HTML file (or React component) - Generate a complete, production-ready HTML file (or React component)
**Outputs:** **Outputs:**
- Full HTML file with embedded CSS and JavaScript - Full HTML file with embedded CSS and JavaScript
- All assets (icons, fonts) are self-contained - All assets (icons, fonts) are self-contained
- Responsive design with mobile-first approach - Responsive design with mobile-first approach
@@ -66,6 +84,7 @@ The **frontend-atomic-design** skill uses the optimized prompt to:
### Step 3: Test the Website ### Step 3: Test the Website
The **ui-ux-testing** skill performs comprehensive testing: The **ui-ux-testing** skill performs comprehensive testing:
- Visual regression testing (captures baseline screenshots) - Visual regression testing (captures baseline screenshots)
- Responsive layout validation (mobile, tablet, desktop) - Responsive layout validation (mobile, tablet, desktop)
- Interactive element testing (buttons, forms, links) - Interactive element testing (buttons, forms, links)
@@ -74,6 +93,7 @@ The **ui-ux-testing** skill performs comprehensive testing:
- User flow validation - User flow validation
**Testing output includes:** **Testing output includes:**
- Screenshots from multiple viewport sizes - Screenshots from multiple viewport sizes
- Detailed findings and issues detected - Detailed findings and issues detected
- Visual regression comparison - Visual regression comparison
@@ -82,12 +102,14 @@ The **ui-ux-testing** skill performs comprehensive testing:
### Step 4: Auto-Fix Detected Issues ### Step 4: Auto-Fix Detected Issues
Any issues detected in testing are automatically fixed: Any issues detected in testing are automatically fixed:
- **Using Haiku 4.5** — A faster model optimized for targeted fixes - **Using Haiku 4.5** — A faster model optimized for targeted fixes
- **HTML-only fixes** — Modifications to structure, styling, or interactivity - **HTML-only fixes** — Modifications to structure, styling, or interactivity
- **Preserves design intent** — Fixes maintain the original design aesthetic - **Preserves design intent** — Fixes maintain the original design aesthetic
- **Re-validates** — Quick verification that fixes resolved the issues - **Re-validates** — Quick verification that fixes resolved the issues
**Common fixes include:** **Common fixes include:**
- Correcting responsive behavior issues - Correcting responsive behavior issues
- Fixing accessibility problems - Fixing accessibility problems
- Adjusting spacing, alignment, or colors - Adjusting spacing, alignment, or colors
@@ -111,23 +133,30 @@ Use this skill whenever you want to:
## How to Trigger This Skill ## How to Trigger This Skill
Simply provide: Simply provide:
1. **Website description** — What kind of website you want (e.g., "e-commerce store for handmade jewelry", "SaaS landing page", "restaurant menu website")
2. **Optional details** — Any specific requirements (colors, features, tone, audience) 1. **Website description** — What kind of website you want (e.g., "e-commerce
store for handmade jewelry", "SaaS landing page", "restaurant menu website")
2. **Optional details** — Any specific requirements (colors, features, tone,
audience)
The skill handles everything else automatically. The skill handles everything else automatically.
## Example Usage ## Example Usage
**User prompt:** "Create a landing page for a sustainable fashion startup called EcoStitch. Include a hero section, features of our eco-friendly materials, pricing plans, customer testimonials, and a newsletter signup." **User prompt:** "Create a landing page for a sustainable fashion startup called
EcoStitch. Include a hero section, features of our eco-friendly materials,
pricing plans, customer testimonials, and a newsletter signup."
**Skill processes:** **Skill processes:**
1. Optimizes prompt with specific design details and layout structure 1. Optimizes prompt with specific design details and layout structure
2. Designs a modern, responsive landing page with all requested sections 2. Designs a modern, responsive landing page with all requested sections
3. Tests layout across mobile/tablet/desktop, forms, links, and visual design 3. Tests layout across mobile/tablet/desktop, forms, links, and visual design
4. Fixes any responsive or interactive issues found 4. Fixes any responsive or interactive issues found
5. Returns production-ready HTML file 5. Returns production-ready HTML file
**Final output:** A complete, tested, bug-free website ready to deploy or customize further. **Final output:** A complete, tested, bug-free website ready to deploy or
customize further.
--- ---
@@ -135,7 +164,8 @@ The skill handles everything else automatically.
### Model Usage ### Model Usage
- **Sonnet 4.6** — Used for optimization, design, and testing (high-quality complex tasks) - **Sonnet 4.6** — Used for optimization, design, and testing (high-quality
complex tasks)
- **Haiku 4.5** — Used for bug fixes only (fast, targeted improvements) - **Haiku 4.5** — Used for bug fixes only (fast, targeted improvements)
### Skill Integration ### Skill Integration
@@ -159,6 +189,7 @@ Final Website (Ready to Use)
### Output Format ### Output Format
The final website is delivered as: The final website is delivered as:
- **HTML file** — Self-contained with CSS and JavaScript embedded - **HTML file** — Self-contained with CSS and JavaScript embedded
- **Screenshots** — Before/after testing comparison - **Screenshots** — Before/after testing comparison
- **Test report** — Issues found and fixes applied - **Test report** — Issues found and fixes applied
@@ -168,20 +199,28 @@ The final website is delivered as:
## Limitations & Notes ## Limitations & Notes
- **Single-page output** — Generates one complete page (though can be expanded to multi-page) - **Single-page output** — Generates one complete page (though can be expanded
to multi-page)
- **Static by default** — Returns HTML; can generate React components if needed - **Static by default** — Returns HTML; can generate React components if needed
- **Database-free** — Forms are functional but don't store data without backend integration - **Database-free** — Forms are functional but don't store data without backend
- **Rapid iteration** — If you want to modify the result, you can iterate by running the skill again with updated requirements integration
- **Rapid iteration** — If you want to modify the result, you can iterate by
running the skill again with updated requirements
--- ---
## Tips for Best Results ## Tips for Best Results
1. **Be descriptive** — More detail in your initial prompt leads to better results 1. **Be descriptive** — More detail in your initial prompt leads to better
2. **Specify audience** — Who is this website for? (target customers, users, etc.) results
3. **Include features** — What should the website do? (e.g., showcase products, collect emails, etc.) 2. **Specify audience** — Who is this website for? (target customers, users,
4. **Mention style** — Any aesthetic preferences? (minimalist, colorful, corporate, playful, etc.) etc.)
5. **Test thoroughly** — Review the testing results to ensure the site meets your needs 3. **Include features** — What should the website do? (e.g., showcase products,
collect emails, etc.)
4. **Mention style** — Any aesthetic preferences? (minimalist, colorful,
corporate, playful, etc.)
5. **Test thoroughly** — Review the testing results to ensure the site meets
your needs
--- ---
@@ -216,6 +255,7 @@ COMPLETE: Return Website + Test Report
## Future Enhancements ## Future Enhancements
Potential expansions to this skill: Potential expansions to this skill:
- Multi-page website generation (homepage, about, services, contact, etc.) - Multi-page website generation (homepage, about, services, contact, etc.)
- CMS integration (connect to content management systems) - CMS integration (connect to content management systems)
- Backend API scaffolding (Node.js/Express templates) - Backend API scaffolding (Node.js/Express templates)
+14 -8
View File
@@ -67,13 +67,15 @@ Dành cho khách hàng:
Dành cho quản lý: Dành cho quản lý:
- Sidebar desktop: Brand, tab navigation (Thực đơn / Combo / Danh mục), link tới Analytics - Sidebar desktop: Brand, tab navigation (Thực đơn / Combo / Danh mục), link tới
Analytics
- CRUD sản phẩm, combo, danh mục qua modals - CRUD sản phẩm, combo, danh mục qua modals
- Auth guard: tự động redirect non-manager về `/` - Auth guard: tự động redirect non-manager về `/`
#### 9. **Trang Phân Tích Tài Chính** (`app/(manager)/manager/analytics`) #### 9. **Trang Phân Tích Tài Chính** (`app/(manager)/manager/analytics`)
- Summary cards: Doanh thu, đơn hàng, lợi nhuận, giá trị đơn trung bình (so sánh kỳ trước) - Summary cards: Doanh thu, đơn hàng, lợi nhuận, giá trị đơn trung bình (so sánh
kỳ trước)
- Bộ chọn kỳ: Ngày / Tuần / Tháng / Năm - Bộ chọn kỳ: Ngày / Tuần / Tháng / Năm
- Biểu đồ SVG thuần: Line, Bar, Pie — hover tooltips tương tác - Biểu đồ SVG thuần: Line, Bar, Pie — hover tooltips tương tác
- Bảng top 5 sản phẩm và bảng chi tiết có thể sắp xếp - Bảng top 5 sản phẩm và bảng chi tiết có thể sắp xếp
@@ -197,7 +199,7 @@ frondend/
## Công Nghệ Sử Dụng ## Công Nghệ Sử Dụng
| Công nghệ | Phiên bản | Mục đích | | Công nghệ | Phiên bản | Mục đích |
| ------------ | --------- | ------------------------------------- | | ---------------- | --------- | ------------------------------------- |
| Next.js | 16.1.7 | React Framework (App Router) | | Next.js | 16.1.7 | React Framework (App Router) |
| React | 19.2.3 | Thư viện UI | | React | 19.2.3 | Thư viện UI |
| TypeScript | ^5 | Kiểu dữ liệu tĩnh | | TypeScript | ^5 | Kiểu dữ liệu tĩnh |
@@ -226,7 +228,7 @@ frondend/
### Tài Khoản Demo ### Tài Khoản Demo
| Loại tài khoản | Tên đăng nhập | Mật khẩu | | Loại tài khoản | Tên đăng nhập | Mật khẩu |
| -------------- | -------------- | -------------- | | -------------- | ------------- | ------------- |
| Manager | admin | admin | | Manager | admin | admin |
| Staff | Nguyễn Văn An | Nguyễn Văn An | | Staff | Nguyễn Văn An | Nguyễn Văn An |
| Customer | 0987654321 | user1 | | Customer | 0987654321 | user1 |
@@ -243,14 +245,18 @@ frondend/
Dự án tuân theo **Atomic Design** pattern — chi tiết tại `Atomic.md`: Dự án tuân theo **Atomic Design** pattern — chi tiết tại `Atomic.md`:
- **Atoms** - Nguyên tố cơ bản: Button, Input, Badge, Text, Heading, Divider - **Atoms** - Nguyên tố cơ bản: Button, Input, Badge, Text, Heading, Divider
- **Molecules** - Nhóm atoms: ProductCard, ShopCard, SearchBar, PaymentSummaryCard - **Molecules** - Nhóm atoms: ProductCard, ShopCard, SearchBar,
- **Organisms** - Phần UI phức tạp: CategorySidebar, CartFab, ProductGrid, ShopGrid, ReviewModal, analytics charts, manager tabs/modals PaymentSummaryCard
- **Templates** - Bố cục trang: MainLayout, AuthLayout, FeedLayout, ManagerLayout - **Organisms** - Phần UI phức tạp: CategorySidebar, CartFab, ProductGrid,
ShopGrid, ReviewModal, analytics charts, manager tabs/modals
- **Templates** - Bố cục trang: MainLayout, AuthLayout, FeedLayout,
ManagerLayout
### Data & Integration ### Data & Integration
- Mock data nằm trong `lib/constants.ts` - Mock data nằm trong `lib/constants.ts`
- Context providers trong `app/providers.tsx`: AuthProvider, MenuProvider, CartProvider - Context providers trong `app/providers.tsx`: AuthProvider, MenuProvider,
CartProvider
- ManagerProvider được thêm bởi `app/(manager)/layout.tsx` - ManagerProvider được thêm bởi `app/(manager)/layout.tsx`
- Thay bằng API calls khi backend sẵn sàng - Thay bằng API calls khi backend sẵn sàng
- Ảnh sản phẩm: thêm vào `public/imgs/products/` - Ảnh sản phẩm: thêm vào `public/imgs/products/`
+1 -1
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import { SHOP_INFO } from "@/lib/constants";
import LoginForm from "@/components/organisms/forms/LoginForm"; import LoginForm from "@/components/organisms/forms/LoginForm";
import { SHOP_INFO } from "@/lib/constants";
import Image from "next/image"; import Image from "next/image";
export default function LoginPage() { export default function LoginPage() {
+3 -6
View File
@@ -1,10 +1,10 @@
"use client"; "use client";
import { SearchBar } from "@/components/molecules/search-bar";
import { CategorySidebar } from "@/components/organisms/navigation"; import { CategorySidebar } from "@/components/organisms/navigation";
import { ProductGrid } from "@/components/organisms/product-grid"; import { ProductGrid } from "@/components/organisms/product-grid";
import { SearchBar } from "@/components/molecules/search-bar";
import { useMenu } from "@/lib/menu-context";
import { MENU_CATEGORIES } from "@/lib/constants"; import { MENU_CATEGORIES } from "@/lib/constants";
import { useMenu } from "@/lib/menu-context";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
/** /**
@@ -75,10 +75,7 @@ export default function Home() {
</div> </div>
{/* ── Product grid (organism handles mobile category menu + grid) ── */} {/* ── Product grid (organism handles mobile category menu + grid) ── */}
<ProductGrid <ProductGrid searchQuery={searchQuery} isSidebarOpen={isSidebarOpen} />
searchQuery={searchQuery}
isSidebarOpen={isSidebarOpen}
/>
</main> </main>
</div> </div>
); );
+15 -6
View File
@@ -48,10 +48,19 @@ export default function PaymentPage() {
<th scope="col" className="px-4 py-3 font-semibold"> <th scope="col" className="px-4 py-3 font-semibold">
Tên sản phẩm Tên sản phẩm
</th> </th>
<th scope="col" className="px-4 py-3 font-semibold">Giá tiền</th> <th scope="col" className="px-4 py-3 font-semibold">
<th scope="col" className="px-4 py-3 font-semibold"> tả</th> Giá tiền
<th scope="col" className="px-4 py-3 font-semibold">Số lượng</th> </th>
<th scope="col" className="px-4 py-3 text-right font-semibold"> <th scope="col" className="px-4 py-3 font-semibold">
tả
</th>
<th scope="col" className="px-4 py-3 font-semibold">
Số lượng
</th>
<th
scope="col"
className="px-4 py-3 text-right font-semibold"
>
Xóa Xóa
</th> </th>
</tr> </tr>
@@ -75,7 +84,7 @@ export default function PaymentPage() {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => decreaseQty(item.id)} onClick={() => decreaseQty(item.id)}
className="inline-flex items-center justify-center h-8 w-8 rounded-lg border border-(--color-border) hover:bg-(--color-border-light)" className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-(--color-border) hover:bg-(--color-border-light)"
aria-label={`Giảm số lượng ${item.name}`} aria-label={`Giảm số lượng ${item.name}`}
> >
- -
@@ -92,7 +101,7 @@ export default function PaymentPage() {
/> />
<button <button
onClick={() => increaseQty(item.id)} onClick={() => increaseQty(item.id)}
className="inline-flex items-center justify-center h-8 w-8 rounded-lg border border-(--color-border) hover:bg-(--color-border-light)" className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-(--color-border) hover:bg-(--color-border-light)"
aria-label={`Tăng số lượng ${item.name}`} aria-label={`Tăng số lượng ${item.name}`}
> >
+ +
+36 -19
View File
@@ -1,7 +1,7 @@
# Components Documentation # Components Documentation
> This project follows **Atomic Design** pattern. > This project follows **Atomic Design** pattern. See `Atomic.md` at project
> See `Atomic.md` at project root for full structure guide. > root for full structure guide.
## Directory Structure ## Directory Structure
@@ -19,17 +19,21 @@ components/
### ProductCard (`molecules/cards/ProductCard.tsx`) ### ProductCard (`molecules/cards/ProductCard.tsx`)
**Description:** Product card molecule. Displays product image, name, description, formatted price, and a Buy button. Width is controlled by the parent grid (w-full). **Description:** Product card molecule. Displays product image, name,
description, formatted price, and a Buy button. Width is controlled by the
parent grid (w-full).
**Atomic level:** Molecule (composed of Button, Caption, Text atoms) **Atomic level:** Molecule (composed of Button, Caption, Text atoms)
### ShopCard (`molecules/cards/ShopCard.tsx`) ### ShopCard (`molecules/cards/ShopCard.tsx`)
**Description:** Shop discovery card. Displays shop image, name, address, and a link to the menu. **Description:** Shop discovery card. Displays shop image, name, address, and a
link to the menu.
### SearchBar (`molecules/search-bar/SearchBar.tsx`) ### SearchBar (`molecules/search-bar/SearchBar.tsx`)
**Description:** Search input with clear button. Controlled component — value and onChange come from parent. **Description:** Search input with clear button. Controlled component — value
and onChange come from parent.
--- ---
@@ -37,30 +41,36 @@ components/
### CategorySidebar (`organisms/navigation/CategorySidebar.tsx`) ### CategorySidebar (`organisms/navigation/CategorySidebar.tsx`)
**Description:** Left sidebar with collapsible category filter. Sticky below header, full viewport height minus header. **Description:** Left sidebar with collapsible category filter. Sticky below
header, full viewport height minus header.
### CartFab (`organisms/cart/CartFab.tsx`) ### CartFab (`organisms/cart/CartFab.tsx`)
**Description:** Floating Action Button displaying cart item count. Links to /payment page. **Description:** Floating Action Button displaying cart item count. Links to
/payment page.
### ProductGrid (`organisms/product-grid/ProductGrid.tsx`) ### ProductGrid (`organisms/product-grid/ProductGrid.tsx`)
**Description:** Full product grid organism with mobile category tabs, filtering, and empty state. Uses useCart and useMenu contexts. **Description:** Full product grid organism with mobile category tabs,
filtering, and empty state. Uses useCart and useMenu contexts.
### ShopGrid (`organisms/shop-grid/ShopGrid.tsx`) ### ShopGrid (`organisms/shop-grid/ShopGrid.tsx`)
**Description:** Responsive grid of ShopCard molecules for the feed/discovery page. **Description:** Responsive grid of ShopCard molecules for the feed/discovery
page.
### ReviewModal (`organisms/modals/ReviewModal.tsx`) ### ReviewModal (`organisms/modals/ReviewModal.tsx`)
**Description:** Modal for customer reviews. Shows 5-star rating and textarea. After submission, displays thank-you message. **Description:** Modal for customer reviews. Shows 5-star rating and textarea.
After submission, displays thank-you message.
### Analytics Charts (`organisms/analytics/`) ### Analytics Charts (`organisms/analytics/`)
**Description:** Pure-SVG chart and table components for the Financial Analytics dashboard. All components are interactive with hover tooltips. **Description:** Pure-SVG chart and table components for the Financial Analytics
dashboard. All components are interactive with hover tooltips.
| Component | File | Description | | Component | File | Description |
|-----------|------|-------------| | ------------ | ---------------- | ---------------------------------------------------------- |
| LineChart | LineChart.tsx | Revenue trend line chart with area fill and hover tooltips | | LineChart | LineChart.tsx | Revenue trend line chart with area fill and hover tooltips |
| BarChart | BarChart.tsx | Grouped bar chart comparing current vs previous period | | BarChart | BarChart.tsx | Grouped bar chart comparing current vs previous period |
| PieChart | PieChart.tsx | Pie chart with interactive legend for category breakdown | | PieChart | PieChart.tsx | Pie chart with interactive legend for category breakdown |
@@ -75,11 +85,13 @@ components/
### MainLayout (`templates/main-layout/MainLayout.tsx`) ### MainLayout (`templates/main-layout/MainLayout.tsx`)
**Description:** Main layout template — wraps content with Header, Footer, and CartFab. Used by (main) route group. **Description:** Main layout template — wraps content with Header, Footer, and
CartFab. Used by (main) route group.
### AuthLayout (`templates/auth-layout/AuthLayout.tsx`) ### AuthLayout (`templates/auth-layout/AuthLayout.tsx`)
**Description:** Auth layout template — centers content in screen. Used by login/register pages. **Description:** Auth layout template — centers content in screen. Used by
login/register pages.
### FeedLayout (`templates/feed-layout/FeedLayout.tsx`) ### FeedLayout (`templates/feed-layout/FeedLayout.tsx`)
@@ -87,7 +99,8 @@ components/
### ManagerLayout (`templates/manager-layout/ManagerLayout.tsx`) ### ManagerLayout (`templates/manager-layout/ManagerLayout.tsx`)
**Description:** Manager layout template — auth guard + ManagerProvider. Used by the manager route group. **Description:** Manager layout template — auth guard + ManagerProvider. Used by
the manager route group.
--- ---
@@ -95,16 +108,20 @@ components/
### AuthContext (`lib/auth-context.tsx`) ### AuthContext (`lib/auth-context.tsx`)
Manages user authentication state including login, logout, and registration. Uses localStorage for persistence. Manages user authentication state including login, logout, and registration.
Uses localStorage for persistence.
### CartContext (`lib/cart-context.tsx`) ### CartContext (`lib/cart-context.tsx`)
Manages shopping cart state with localStorage persistence. Tracks items, quantities, and totals. Manages shopping cart state with localStorage persistence. Tracks items,
quantities, and totals.
### MenuContext (`lib/menu-context.tsx`) ### MenuContext (`lib/menu-context.tsx`)
Provides shared activeCategory state across components. Synchronizes Header mobile menu and CategorySidebar selection. Provides shared activeCategory state across components. Synchronizes Header
mobile menu and CategorySidebar selection.
### ManagerContext (`lib/manager-context.tsx`) ### ManagerContext (`lib/manager-context.tsx`)
Manages menu CRUD state (products, combos, categories) for the manager dashboard. Manages menu CRUD state (products, combos, categories) for the manager
dashboard.
+19
View File
@@ -42,6 +42,7 @@ Main interactive button component with multiple variants and sizes.
- All standard HTML button attributes - All standard HTML button attributes
**Usage:** **Usage:**
```tsx ```tsx
import { Button } from "@/components/atoms"; import { Button } from "@/components/atoms";
@@ -59,6 +60,7 @@ import { Button } from "@/components/atoms";
``` ```
**Styling:** **Styling:**
- Primary: Branded color with dark hover - Primary: Branded color with dark hover
- Secondary: Border style with light background on hover - Secondary: Border style with light background on hover
- Danger: Red for destructive actions - Danger: Red for destructive actions
@@ -78,6 +80,7 @@ Button designed specifically for icon-only interactions.
- All standard HTML button attributes - All standard HTML button attributes
**Usage:** **Usage:**
```tsx ```tsx
import { IconButton } from "@/components/atoms"; import { IconButton } from "@/components/atoms";
@@ -102,6 +105,7 @@ General text input field with optional label, error, and icon.
- All standard HTML input attributes - All standard HTML input attributes
**Usage:** **Usage:**
```tsx ```tsx
import { TextInput } from "@/components/atoms"; import { TextInput } from "@/components/atoms";
@@ -135,6 +139,7 @@ Search input with built-in search icon and clear button.
- All standard HTML input attributes - All standard HTML input attributes
**Usage:** **Usage:**
```tsx ```tsx
import { SearchInput } from "@/components/atoms"; import { SearchInput } from "@/components/atoms";
@@ -161,6 +166,7 @@ Multi-line text input with optional label and error.
- All standard HTML textarea attributes - All standard HTML textarea attributes
**Usage:** **Usage:**
```tsx ```tsx
import { Textarea } from "@/components/atoms"; import { Textarea } from "@/components/atoms";
@@ -188,6 +194,7 @@ Semantic heading component with level-based sizing.
- All standard HTML heading attributes - All standard HTML heading attributes
**Sizing:** **Sizing:**
- Level 1: text-3xl font-bold - Level 1: text-3xl font-bold
- Level 2: text-2xl font-bold - Level 2: text-2xl font-bold
- Level 3: text-xl font-bold - Level 3: text-xl font-bold
@@ -196,6 +203,7 @@ Semantic heading component with level-based sizing.
- Level 6: text-sm font-semibold - Level 6: text-sm font-semibold
**Usage:** **Usage:**
```tsx ```tsx
import { Heading } from "@/components/atoms"; import { Heading } from "@/components/atoms";
@@ -217,12 +225,14 @@ Paragraph text with semantic variants.
- All standard HTML paragraph attributes - All standard HTML paragraph attributes
**Variants:** **Variants:**
- body1: text-base (main content) - body1: text-base (main content)
- body2: text-sm (secondary content) - body2: text-sm (secondary content)
- caption: text-xs (smallest text) - caption: text-xs (smallest text)
- label: text-sm font-medium (form labels) - label: text-sm font-medium (form labels)
**Usage:** **Usage:**
```tsx ```tsx
import { Text } from "@/components/atoms"; import { Text } from "@/components/atoms";
@@ -244,6 +254,7 @@ Small caption/note text component.
- All standard HTML span attributes - All standard HTML span attributes
**Usage:** **Usage:**
```tsx ```tsx
import { Caption } from "@/components/atoms"; import { Caption } from "@/components/atoms";
@@ -268,6 +279,7 @@ Labeled badge component for highlighting information.
- All standard HTML span attributes - All standard HTML span attributes
**Variants:** **Variants:**
- primary: Branded color - primary: Branded color
- secondary: Light gray - secondary: Light gray
- success: Green - success: Green
@@ -275,6 +287,7 @@ Labeled badge component for highlighting information.
- warning: Yellow - warning: Yellow
**Usage:** **Usage:**
```tsx ```tsx
import { Badge } from "@/components/atoms"; import { Badge } from "@/components/atoms";
@@ -296,6 +309,7 @@ Specialized badge for displaying formatted prices.
- All standard HTML span attributes - All standard HTML span attributes
**Usage:** **Usage:**
```tsx ```tsx
import { PriceBadge } from "@/components/atoms"; import { PriceBadge } from "@/components/atoms";
@@ -304,6 +318,7 @@ import { PriceBadge } from "@/components/atoms";
``` ```
**Output:** **Output:**
- VND: "50.000 ₫" - VND: "50.000 ₫"
- USD: "$99.99" - USD: "$99.99"
@@ -321,6 +336,7 @@ Visual separator element.
- All standard HTML hr attributes - All standard HTML hr attributes
**Usage:** **Usage:**
```tsx ```tsx
import { Divider } from "@/components/atoms"; import { Divider } from "@/components/atoms";
@@ -414,6 +430,7 @@ import type {
``` ```
### Product Card ### Product Card
```tsx ```tsx
<div className="rounded-lg border p-4"> <div className="rounded-lg border p-4">
<Heading level={3}>Product Name</Heading> <Heading level={3}>Product Name</Heading>
@@ -426,6 +443,7 @@ import type {
``` ```
### Rating Display ### Rating Display
```tsx ```tsx
<div> <div>
<Heading level={4}>Reviews</Heading> <Heading level={4}>Reviews</Heading>
@@ -439,6 +457,7 @@ import type {
## 🧪 Testing Atoms ## 🧪 Testing Atoms
### Props Validation ### Props Validation
```tsx ```tsx
// ✅ Valid // ✅ Valid
<Button variant="primary" size="sm">Click</Button> <Button variant="primary" size="sm">Click</Button>
@@ -13,7 +13,6 @@ export default function PaymentSummaryCard({
isCustomer = false, isCustomer = false,
backHref, backHref,
}: PaymentSummaryCardProps) { }: PaymentSummaryCardProps) {
const [isReviewOpen, setIsReviewOpen] = useState(false); const [isReviewOpen, setIsReviewOpen] = useState(false);
const handlePayment = () => { const handlePayment = () => {
// UI-only: open review modal after "payment" // UI-only: open review modal after "payment"
+7 -1
View File
@@ -62,7 +62,13 @@ export default function ProductCard({
<Text variant="body2" className="font-bold"> <Text variant="body2" className="font-bold">
{formattedPrice} {formattedPrice}
</Text> </Text>
<Button onClick={onBuy} variant="primary" size="sm" icon="fa-cart-plus" aria-label={`Mua ${productName}`}> <Button
onClick={onBuy}
variant="primary"
size="sm"
icon="fa-cart-plus"
aria-label={`Mua ${productName}`}
>
Mua Mua
</Button> </Button>
</div> </div>
+8 -2
View File
@@ -16,10 +16,16 @@ interface BarChartProps {
* Tooltip auto-flips above/below bar top to stay inside the viewBox. * Tooltip auto-flips above/below bar top to stay inside the viewBox.
*/ */
export function BarChart({ current, previous, height = 200 }: BarChartProps) { export function BarChart({ current, previous, height = 200 }: BarChartProps) {
const [hovered, setHovered] = useState<{ set: "cur" | "prev"; idx: number } | null>(null); const [hovered, setHovered] = useState<{
set: "cur" | "prev";
idx: number;
} | null>(null);
const W = 800; const W = 800;
const H = height; const H = height;
const padL = 56, padR = 16, padT = 16, padB = 40; const padL = 56,
padR = 16,
padT = 16,
padB = 40;
const chartW = W - padL - padR; const chartW = W - padL - padR;
const chartH = H - padT - padB; const chartH = H - padT - padB;
+93 -15
View File
@@ -18,7 +18,10 @@ export function LineChart({ data, height = 200 }: LineChartProps) {
const [hovered, setHovered] = useState<number | null>(null); const [hovered, setHovered] = useState<number | null>(null);
const W = 800; const W = 800;
const H = height; const H = height;
const padL = 56, padR = 16, padT = 16, padB = 40; const padL = 56,
padR = 16,
padT = 16,
padB = 40;
const chartW = W - padL - padR; const chartW = W - padL - padR;
const chartH = H - padT - padB; const chartH = H - padT - padB;
@@ -66,44 +69,119 @@ export function LineChart({ data, height = 200 }: LineChartProps) {
{gridLines.map((g, i) => ( {gridLines.map((g, i) => (
<g key={i}> <g key={i}>
<line x1={padL} y1={g.y} x2={W - padR} y2={g.y} stroke="#E2C9A8" strokeWidth="1" strokeDasharray={i === yTicks ? "0" : "4 3"} /> <line
<text x={padL - 6} y={g.y + 4} textAnchor="end" fontSize="10" fill="#A08060">{formatCurrency(g.val)}</text> x1={padL}
y1={g.y}
x2={W - padR}
y2={g.y}
stroke="#E2C9A8"
strokeWidth="1"
strokeDasharray={i === yTicks ? "0" : "4 3"}
/>
<text
x={padL - 6}
y={g.y + 4}
textAnchor="end"
fontSize="10"
fill="#A08060"
>
{formatCurrency(g.val)}
</text>
</g> </g>
))} ))}
<path d={areaD} fill="url(#areaGrad)" /> <path d={areaD} fill="url(#areaGrad)" />
<path d={pathD} fill="none" stroke="#6F4E37" strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" /> <path
d={pathD}
fill="none"
stroke="#6F4E37"
strokeWidth="2.5"
strokeLinejoin="round"
strokeLinecap="round"
/>
{points.map((p, i) => {points.map((p, i) =>
i % step === 0 ? ( i % step === 0 ? (
<text key={i} x={p.x} y={H - 8} textAnchor="middle" fontSize="10" fill="#A08060">{p.data.label}</text> <text
key={i}
x={p.x}
y={H - 8}
textAnchor="middle"
fontSize="10"
fill="#A08060"
>
{p.data.label}
</text>
) : null, ) : null,
)} )}
{points.map((p) => ( {points.map((p) => (
<circle <circle
key={p.index} key={p.index}
cx={p.x} cy={p.y} cx={p.x}
cy={p.y}
r={hovered === p.index ? 5 : 3} r={hovered === p.index ? 5 : 3}
fill={hovered === p.index ? "#C8973A" : "#6F4E37"} fill={hovered === p.index ? "#C8973A" : "#6F4E37"}
stroke="#FDF6EC" strokeWidth="2" stroke="#FDF6EC"
strokeWidth="2"
style={{ cursor: "pointer", transition: "r 150ms" }} style={{ cursor: "pointer", transition: "r 150ms" }}
onMouseEnter={() => setHovered(p.index)} onMouseEnter={() => setHovered(p.index)}
/> />
))} ))}
{hovered !== null && (() => { {hovered !== null &&
(() => {
const p = points[hovered]; const p = points[hovered];
const tipW = 120, tipH = 48; const tipW = 120,
const tipX = Math.min(Math.max(p.x - tipW / 2, padL), W - padR - tipW); tipH = 48;
const tipX = Math.min(
Math.max(p.x - tipW / 2, padL),
W - padR - tipW,
);
const aboveY = p.y - tipH - 10; const aboveY = p.y - tipH - 10;
const tipY = Math.min(Math.max(aboveY >= padT ? aboveY : p.y + 10, padT), padT + chartH - tipH); const tipY = Math.min(
Math.max(aboveY >= padT ? aboveY : p.y + 10, padT),
padT + chartH - tipH,
);
return ( return (
<g> <g>
<rect x={tipX} y={tipY} width={tipW} height={tipH} rx="6" fill="#3D2B1F" opacity="0.92" /> <rect
<text x={tipX + tipW / 2} y={tipY + 16} textAnchor="middle" fontSize="10" fill="#F0D9A8">{p.data.label}</text> x={tipX}
<text x={tipX + tipW / 2} y={tipY + 30} textAnchor="middle" fontSize="11" fontWeight="600" fill="#C8973A">{formatCurrency(p.data.revenue)}</text> y={tipY}
<text x={tipX + tipW / 2} y={tipY + 44} textAnchor="middle" fontSize="10" fill="#A08060">{p.data.orders} đơn</text> width={tipW}
height={tipH}
rx="6"
fill="#3D2B1F"
opacity="0.92"
/>
<text
x={tipX + tipW / 2}
y={tipY + 16}
textAnchor="middle"
fontSize="10"
fill="#F0D9A8"
>
{p.data.label}
</text>
<text
x={tipX + tipW / 2}
y={tipY + 30}
textAnchor="middle"
fontSize="11"
fontWeight="600"
fill="#C8973A"
>
{formatCurrency(p.data.revenue)}
</text>
<text
x={tipX + tipW / 2}
y={tipY + 44}
textAnchor="middle"
fontSize="10"
fill="#A08060"
>
{p.data.orders} đơn
</text>
</g> </g>
); );
})()} })()}
+1 -1
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useMemo } from "react"; import { useMemo, useState } from "react";
export interface PieSlice { export interface PieSlice {
label: string; label: string;
+1 -1
View File
@@ -1,10 +1,10 @@
import Button from "@/components/atoms/buttons/Button";
import ErrorMessageLogin from "@/components/atoms/errors/ErrorMessageLogin"; import ErrorMessageLogin from "@/components/atoms/errors/ErrorMessageLogin";
import LoginInput from "@/components/atoms/inputs/LoginInput"; import LoginInput from "@/components/atoms/inputs/LoginInput";
import { useAuth } from "@/lib/auth-context"; import { useAuth } from "@/lib/auth-context";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { FormEvent, useState } from "react"; import { FormEvent, useState } from "react";
import Button from "@/components/atoms/buttons/Button";
export default function LoginForm() { export default function LoginForm() {
const router = useRouter(); const router = useRouter();
+4 -1
View File
@@ -152,7 +152,10 @@ export default function ProductsTab() {
</tr> </tr>
) : ( ) : (
filtered.map((p) => ( filtered.map((p) => (
<tr key={p.id} className="hover:bg-background transition-colors"> <tr
key={p.id}
className="hover:bg-background transition-colors"
>
<td className="px-4 py-3"> <td className="px-4 py-3">
<div> <div>
<p className="text-foreground font-medium">{p.name}</p> <p className="text-foreground font-medium">{p.name}</p>
+1 -1
View File
@@ -16,7 +16,7 @@ spec:
spec: spec:
containers: containers:
- name: frontend-container - name: frontend-container
image: git.demonkernel.io.vn/foodsurf/frontend:1.1.2 image: git.demonkernel.io.vn/foodsurf/frontend:1.2.0
ports: ports:
- containerPort: 3000 - containerPort: 3000
resources: resources:
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "temp", "name": "temp",
"version": "1.1.2", "version": "1.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "temp", "name": "temp",
"version": "1.1.2", "version": "1.2.0",
"dependencies": { "dependencies": {
"@tailwindcss/postcss": "^4.2.2", "@tailwindcss/postcss": "^4.2.2",
"@types/node": "^20.19.37", "@types/node": "^20.19.37",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "temp", "name": "temp",
"version": "1.1.2", "version": "1.2.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@@ -30,7 +30,7 @@
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"eslint": "^9.39.4", "eslint": "^9.39.4",
"eslint-config-next": "16.1.7", "eslint-config-next": "16.1.7",
"prettier": "^3.8.2", "prettier": "^3.8.3",
"prettier-plugin-embed": "^0.5.1", "prettier-plugin-embed": "^0.5.1",
"prettier-plugin-groovy": "^0.2.1", "prettier-plugin-groovy": "^0.2.1",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
+64 -55
View File
@@ -20,6 +20,9 @@ importers:
next: next:
specifier: 16.1.7 specifier: 16.1.7
version: 16.1.7(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) version: 16.1.7(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
nextjs:
specifier: ^0.0.3
version: 0.0.3
react: react:
specifier: 19.2.3 specifier: 19.2.3
version: 19.2.3 version: 19.2.3
@@ -47,7 +50,7 @@ importers:
version: 13.1.5(semantic-release@25.0.3(typescript@5.9.3)) version: 13.1.5(semantic-release@25.0.3(typescript@5.9.3))
'@trivago/prettier-plugin-sort-imports': '@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.2(prettier@3.8.2) version: 6.0.2(prettier@3.8.3)
'@types/react-dom': '@types/react-dom':
specifier: ^19.2.3 specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.14) version: 19.2.3(@types/react@19.2.14)
@@ -58,17 +61,17 @@ importers:
specifier: 16.1.7 specifier: 16.1.7
version: 16.1.7(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) version: 16.1.7(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
prettier: prettier:
specifier: ^3.8.2 specifier: ^3.8.3
version: 3.8.2 version: 3.8.3
prettier-plugin-embed: prettier-plugin-embed:
specifier: ^0.5.1 specifier: ^0.5.1
version: 0.5.1 version: 0.5.1
prettier-plugin-groovy: prettier-plugin-groovy:
specifier: ^0.2.1 specifier: ^0.2.1
version: 0.2.1(prettier@3.8.2) version: 0.2.1(prettier@3.8.3)
prettier-plugin-tailwindcss: prettier-plugin-tailwindcss:
specifier: ^0.7.2 specifier: ^0.7.2
version: 0.7.2(@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.2))(prettier@3.8.2) version: 0.7.2(@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3))(prettier@3.8.3)
semantic-release: semantic-release:
specifier: ^25.0.3 specifier: ^25.0.3
version: 25.0.3(typescript@5.9.3) version: 25.0.3(typescript@5.9.3)
@@ -171,11 +174,11 @@ packages:
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'} engines: {node: '>=0.1.90'}
'@emnapi/core@1.9.2': '@emnapi/core@1.10.0':
resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
'@emnapi/runtime@1.9.2': '@emnapi/runtime@1.10.0':
resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
'@emnapi/wasi-threads@1.2.1': '@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
@@ -1092,8 +1095,8 @@ packages:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22} engines: {node: 18 || 20 || >=22}
baseline-browser-mapping@2.10.18: baseline-browser-mapping@2.10.19:
resolution: {integrity: sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==} resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
@@ -1171,8 +1174,8 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
caniuse-lite@1.0.30001787: caniuse-lite@1.0.30001788:
resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==}
chalk@2.4.1: chalk@2.4.1:
resolution: {integrity: sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==} resolution: {integrity: sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==}
@@ -1463,8 +1466,8 @@ packages:
ee-first@1.1.1: ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.336: electron-to-chromium@1.5.340:
resolution: {integrity: sha512-AbH9q9J455r/nLmdNZes0G0ZKcRX73FicwowalLs6ijwOmCJSRRrLX63lcAlzy9ux3dWK1w1+1nsBJEWN11hcQ==} resolution: {integrity: sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==}
emoji-regex@10.6.0: emoji-regex@10.6.0:
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
@@ -1620,11 +1623,11 @@ packages:
peerDependencies: peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
eslint-plugin-react-hooks@7.0.1: eslint-plugin-react-hooks@7.1.0:
resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} resolution: {integrity: sha512-LDicyhrRFrIaheDYryeM2W8gWyZXnAs4zIr2WVPiOSeTmIu2RjR4x/9N0xLaRWZ+9hssBDGo3AadcohuzAvSvg==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0
eslint-plugin-react@7.37.5: eslint-plugin-react@7.37.5:
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
@@ -1889,8 +1892,8 @@ packages:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
get-tsconfig@4.13.7: get-tsconfig@4.14.0:
resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
git-log-parser@1.2.1: git-log-parser@1.2.1:
resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==}
@@ -2649,6 +2652,10 @@ packages:
sass: sass:
optional: true optional: true
nextjs@0.0.3:
resolution: {integrity: sha512-mYbDUo4/sRAZ8TqK63PCpYnFiLg7BICG/ot9+guOrUKd4/Fo71ZmEQ41IZbH6nqbQvG7SXTBuofJXAIWfNho0w==}
engines: {node: '>=0.8.21'}
nocache@2.0.0: nocache@2.0.0:
resolution: {integrity: sha512-YdKcy2x0dDwOh+8BEuHvA+mnOKAhmMQDgKBOCUGaLpewdmsRYguYZSom3yA+/OrE61O/q+NMQANnun65xpI1Hw==} resolution: {integrity: sha512-YdKcy2x0dDwOh+8BEuHvA+mnOKAhmMQDgKBOCUGaLpewdmsRYguYZSom3yA+/OrE61O/q+NMQANnun65xpI1Hw==}
@@ -2992,8 +2999,8 @@ packages:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
postcss@8.5.9: postcss@8.5.10:
resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1: prelude-ls@1.2.1:
@@ -3064,8 +3071,8 @@ packages:
prettier-plugin-svelte: prettier-plugin-svelte:
optional: true optional: true
prettier@3.8.2: prettier@3.8.3:
resolution: {integrity: sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==} resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@@ -3634,8 +3641,8 @@ packages:
undici-types@6.21.0: undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
undici@6.24.1: undici@6.25.0:
resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==}
engines: {node: '>=18.17'} engines: {node: '>=18.17'}
undici@7.25.0: undici@7.25.0:
@@ -3845,7 +3852,7 @@ snapshots:
'@actions/http-client@4.0.0': '@actions/http-client@4.0.0':
dependencies: dependencies:
tunnel: 0.0.6 tunnel: 0.0.6
undici: 6.24.1 undici: 6.25.0
'@actions/io@3.0.2': {} '@actions/io@3.0.2': {}
@@ -3966,13 +3973,13 @@ snapshots:
'@colors/colors@1.5.0': '@colors/colors@1.5.0':
optional: true optional: true
'@emnapi/core@1.9.2': '@emnapi/core@1.10.0':
dependencies: dependencies:
'@emnapi/wasi-threads': 1.2.1 '@emnapi/wasi-threads': 1.2.1
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
'@emnapi/runtime@1.9.2': '@emnapi/runtime@1.10.0':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
@@ -4124,7 +4131,7 @@ snapshots:
'@img/sharp-wasm32@0.34.5': '@img/sharp-wasm32@0.34.5':
dependencies: dependencies:
'@emnapi/runtime': 1.9.2 '@emnapi/runtime': 1.10.0
optional: true optional: true
'@img/sharp-win32-arm64@0.34.5': '@img/sharp-win32-arm64@0.34.5':
@@ -4157,8 +4164,8 @@ snapshots:
'@napi-rs/wasm-runtime@0.2.12': '@napi-rs/wasm-runtime@0.2.12':
dependencies: dependencies:
'@emnapi/core': 1.9.2 '@emnapi/core': 1.10.0
'@emnapi/runtime': 1.9.2 '@emnapi/runtime': 1.10.0
'@tybys/wasm-util': 0.10.1 '@tybys/wasm-util': 0.10.1
optional: true optional: true
@@ -4468,10 +4475,10 @@ snapshots:
'@alloc/quick-lru': 5.2.0 '@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.2.2 '@tailwindcss/node': 4.2.2
'@tailwindcss/oxide': 4.2.2 '@tailwindcss/oxide': 4.2.2
postcss: 8.5.9 postcss: 8.5.10
tailwindcss: 4.2.2 tailwindcss: 4.2.2
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.2)': '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3)':
dependencies: dependencies:
'@babel/generator': 7.29.1 '@babel/generator': 7.29.1
'@babel/parser': 7.29.2 '@babel/parser': 7.29.2
@@ -4481,7 +4488,7 @@ snapshots:
lodash-es: 4.18.1 lodash-es: 4.18.1
minimatch: 9.0.9 minimatch: 9.0.9
parse-imports-exports: 0.2.4 parse-imports-exports: 0.2.4
prettier: 3.8.2 prettier: 3.8.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -4874,7 +4881,7 @@ snapshots:
balanced-match@4.0.4: {} balanced-match@4.0.4: {}
baseline-browser-mapping@2.10.18: {} baseline-browser-mapping@2.10.19: {}
basic-auth@2.0.1: basic-auth@2.0.1:
dependencies: dependencies:
@@ -4924,9 +4931,9 @@ snapshots:
browserslist@4.28.2: browserslist@4.28.2:
dependencies: dependencies:
baseline-browser-mapping: 2.10.18 baseline-browser-mapping: 2.10.19
caniuse-lite: 1.0.30001787 caniuse-lite: 1.0.30001788
electron-to-chromium: 1.5.336 electron-to-chromium: 1.5.340
node-releases: 2.0.37 node-releases: 2.0.37
update-browserslist-db: 1.2.3(browserslist@4.28.2) update-browserslist-db: 1.2.3(browserslist@4.28.2)
@@ -4970,7 +4977,7 @@ snapshots:
callsites@3.1.0: {} callsites@3.1.0: {}
caniuse-lite@1.0.30001787: {} caniuse-lite@1.0.30001788: {}
chalk@2.4.1: chalk@2.4.1:
dependencies: dependencies:
@@ -5255,7 +5262,7 @@ snapshots:
ee-first@1.1.1: {} ee-first@1.1.1: {}
electron-to-chromium@1.5.336: {} electron-to-chromium@1.5.340: {}
emoji-regex@10.6.0: {} emoji-regex@10.6.0: {}
@@ -5411,7 +5418,7 @@ snapshots:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: 7.1.0(eslint@9.39.4(jiti@2.6.1))
globals: 16.4.0 globals: 16.4.0
typescript-eslint: 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) typescript-eslint: 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
optionalDependencies: optionalDependencies:
@@ -5435,7 +5442,7 @@ snapshots:
'@nolyfill/is-core-module': 1.0.39 '@nolyfill/is-core-module': 1.0.39
debug: 4.4.3 debug: 4.4.3
eslint: 9.39.4(jiti@2.6.1) eslint: 9.39.4(jiti@2.6.1)
get-tsconfig: 4.13.7 get-tsconfig: 4.14.0
is-bun-module: 2.0.0 is-bun-module: 2.0.0
stable-hash: 0.0.5 stable-hash: 0.0.5
tinyglobby: 0.2.16 tinyglobby: 0.2.16
@@ -5504,7 +5511,7 @@ snapshots:
safe-regex-test: 1.1.0 safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1 string.prototype.includes: 2.0.1
eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)): eslint-plugin-react-hooks@7.1.0(eslint@9.39.4(jiti@2.6.1)):
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
'@babel/parser': 7.29.2 '@babel/parser': 7.29.2
@@ -5882,7 +5889,7 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
get-intrinsic: 1.3.0 get-intrinsic: 1.3.0
get-tsconfig@4.13.7: get-tsconfig@4.14.0:
dependencies: dependencies:
resolve-pkg-maps: 1.0.0 resolve-pkg-maps: 1.0.0
@@ -6579,8 +6586,8 @@ snapshots:
dependencies: dependencies:
'@next/env': 16.1.7 '@next/env': 16.1.7
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.18 baseline-browser-mapping: 2.10.19
caniuse-lite: 1.0.30001787 caniuse-lite: 1.0.30001788
postcss: 8.4.31 postcss: 8.4.31
react: 19.2.3 react: 19.2.3
react-dom: 19.2.3(react@19.2.3) react-dom: 19.2.3(react@19.2.3)
@@ -6599,6 +6606,8 @@ snapshots:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros
nextjs@0.0.3: {}
nocache@2.0.0: {} nocache@2.0.0: {}
node-emoji@2.2.0: node-emoji@2.2.0:
@@ -6862,7 +6871,7 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
postcss@8.5.9: postcss@8.5.10:
dependencies: dependencies:
nanoid: 3.3.11 nanoid: 3.3.11
picocolors: 1.1.1 picocolors: 1.1.1
@@ -6881,17 +6890,17 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- babel-plugin-macros - babel-plugin-macros
prettier-plugin-groovy@0.2.1(prettier@3.8.2): prettier-plugin-groovy@0.2.1(prettier@3.8.3):
dependencies: dependencies:
prettier: 3.8.2 prettier: 3.8.3
prettier-plugin-tailwindcss@0.7.2(@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.2))(prettier@3.8.2): prettier-plugin-tailwindcss@0.7.2(@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.3))(prettier@3.8.3):
dependencies: dependencies:
prettier: 3.8.2 prettier: 3.8.3
optionalDependencies: optionalDependencies:
'@trivago/prettier-plugin-sort-imports': 6.0.2(prettier@3.8.2) '@trivago/prettier-plugin-sort-imports': 6.0.2(prettier@3.8.3)
prettier@3.8.2: {} prettier@3.8.3: {}
pretty-ms@9.3.0: pretty-ms@9.3.0:
dependencies: dependencies:
@@ -7617,7 +7626,7 @@ snapshots:
undici-types@6.21.0: {} undici-types@6.21.0: {}
undici@6.24.1: {} undici@6.25.0: {}
undici@7.25.0: {} undici@7.25.0: {}