Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
517 changes: 517 additions & 0 deletions FSD.md

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
"private": true,
"version": "0.0.0",
"type": "module",
"homepage": "https://toeam.github.io/front_7th_chapter3-3/",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"test": "vitest",
"coverage": "vitest run --coverage"
"coverage": "vitest run --coverage",
"predeploy": "pnpm run build",
"deploy": "gh-pages -d dist"
},
"dependencies": {
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
"react": "^19.2.1",
"react-dom": "^19.2.1"
"react-dom": "^19.2.1",
"zustand": "^5.0.9"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
Expand All @@ -30,6 +36,7 @@
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"gh-pages": "^6.3.0",
"globals": "^16.5.0",
"jsdom": "^27.2.0",
"lucide-react": "^0.556.0",
Expand Down
1,867 changes: 944 additions & 923 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

28 changes: 17 additions & 11 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { BrowserRouter as Router } from "react-router-dom"
import Header from "./components/Header.tsx"
import Footer from "./components/Footer.tsx"
import { QueryClientProvider } from "@tanstack/react-query"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import { queryClient } from "./shared/lib"
import Header from "./shared/ui/Header.tsx"
import Footer from "./shared/ui/Footer.tsx"
import PostsManagerPage from "./pages/PostsManagerPage.tsx"

const App = () => {
return (
<Router>
<div className="flex flex-col min-h-screen">
<Header />
<main className="flex-grow container mx-auto px-4 py-8">
<PostsManagerPage />
</main>
<Footer />
</div>
</Router>
<QueryClientProvider client={queryClient}>
<Router>
<div className="flex flex-col min-h-screen">
<Header />
<main className="flex-grow container mx-auto px-4 py-8">
<PostsManagerPage />
</main>
<Footer />
</div>
</Router>
{import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} />}
</QueryClientProvider>
)
}

Expand Down
169 changes: 104 additions & 65 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,57 +42,72 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ className, v
Button.displayName = "Button"

// 입력 컴포넌트
export const Input = forwardRef(({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={`flex h-10 w-full rounded-md border border-input bg-white px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
ref={ref}
{...props}
/>
)
})
export const Input = forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={`flex h-10 w-full rounded-md border border-input bg-white px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"

// 카드 컴포넌트
export const Card = forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={`rounded-lg border bg-card text-card-foreground shadow-sm ${className}`} {...props} />
))
export const Card = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={`rounded-lg border bg-card text-card-foreground shadow-sm ${className}`} {...props} />
)
)
Card.displayName = "Card"

export const CardHeader = forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={`flex flex-col space-y-1.5 p-6 ${className}`} {...props} />
))
export const CardHeader = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={`flex flex-col space-y-1.5 p-6 ${className}`} {...props} />
)
)
CardHeader.displayName = "CardHeader"

export const CardTitle = forwardRef(({ className, ...props }, ref) => (
<h3 ref={ref} className={`text-2xl font-semibold leading-none tracking-tight ${className}`} {...props} />
))
export const CardTitle = forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
({ className, ...props }, ref) => (
<h3 ref={ref} className={`text-2xl font-semibold leading-none tracking-tight ${className}`} {...props} />
)
)
CardTitle.displayName = "CardTitle"

export const CardContent = forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={`p-6 pt-0 ${className}`} {...props} />
))
export const CardContent = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={`p-6 pt-0 ${className}`} {...props} />
)
)
CardContent.displayName = "CardContent"

// 텍스트 영역 컴포넌트
export const Textarea = forwardRef(({ className, ...props }, ref) => {
return (
<textarea
className={`flex min-h-[150px] w-full rounded-md border border-input bg-white px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
ref={ref}
{...props}
/>
)
})
export const Textarea = forwardRef<HTMLTextAreaElement, React.TextareaHTMLAttributes<HTMLTextAreaElement>>(
({ className, ...props }, ref) => {
return (
<textarea
className={`flex min-h-[150px] w-full rounded-md border border-input bg-white px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"

// 선택 컴포넌트
export const Select = SelectPrimitive.Root
export const SelectGroup = SelectPrimitive.Group
export const SelectValue = SelectPrimitive.Value

export const SelectTrigger = forwardRef(({ className, children, ...props }, ref) => (
export const SelectTrigger = forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={`flex h-10 items-center justify-between rounded-md border border-input bg-white px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
Expand All @@ -104,7 +119,10 @@ export const SelectTrigger = forwardRef(({ className, children, ...props }, ref)
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName

export const SelectContent = forwardRef(({ className, children, position = "popper", ...props }, ref) => (
export const SelectContent = forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
Expand All @@ -118,7 +136,10 @@ export const SelectContent = forwardRef(({ className, children, position = "popp
))
SelectContent.displayName = SelectPrimitive.Content.displayName

export const SelectItem = forwardRef(({ className, children, ...props }, ref) => (
export const SelectItem = forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={`relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 ${className}`}
Expand All @@ -140,7 +161,10 @@ export const DialogTrigger = DialogPrimitive.Trigger
export const DialogPortal = DialogPrimitive.Portal
export const DialogOverlay = DialogPrimitive.Overlay

export const DialogContent = forwardRef(({ className, children, ...props }, ref) => (
export const DialogContent = forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay className="fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />
<DialogPrimitive.Content
Expand All @@ -158,12 +182,15 @@ export const DialogContent = forwardRef(({ className, children, ...props }, ref)
))
DialogContent.displayName = DialogPrimitive.Content.displayName

export const DialogHeader = ({ className, ...props }) => (
export const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={`flex flex-col space-y-1.5 text-center sm:text-left ${className}`} {...props} />
)
DialogHeader.displayName = "DialogHeader"

export const DialogTitle = forwardRef(({ className, ...props }, ref) => (
export const DialogTitle = forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={`text-lg font-semibold leading-none tracking-tight ${className}`}
Expand All @@ -173,42 +200,54 @@ export const DialogTitle = forwardRef(({ className, ...props }, ref) => (
DialogTitle.displayName = DialogPrimitive.Title.displayName

// 테이블 컴포넌트
export const Table = forwardRef(({ className, ...props }, ref) => (
<div className="w-full overflow-auto">
<table ref={ref} className={`table-fixed w-full caption-bottom text-sm ${className}`} {...props} />
</div>
))
export const Table = forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => (
<div className="w-full overflow-auto">
<table ref={ref} className={`table-fixed w-full caption-bottom text-sm ${className}`} {...props} />
</div>
)
)
Table.displayName = "Table"

export const TableHeader = forwardRef(({ className, ...props }, ref) => (
<thead ref={ref} className={`[&_tr]:border-b ${className}`} {...props} />
))
export const TableHeader = forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<thead ref={ref} className={`[&_tr]:border-b ${className}`} {...props} />
)
)
TableHeader.displayName = "TableHeader"

export const TableBody = forwardRef(({ className, ...props }, ref) => (
<tbody ref={ref} className={`[&_tr:last-child]:border-0 ${className}`} {...props} />
))
export const TableBody = forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tbody ref={ref} className={`[&_tr:last-child]:border-0 ${className}`} {...props} />
)
)
TableBody.displayName = "TableBody"

export const TableRow = forwardRef(({ className, ...props }, ref) => (
<tr
ref={ref}
className={`border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted h-14 ${className}`}
{...props}
/>
))
export const TableRow = forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => (
<tr
ref={ref}
className={`border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted h-14 ${className}`}
{...props}
/>
)
)
TableRow.displayName = "TableRow"

export const TableHead = forwardRef(({ className, ...props }, ref) => (
<th
ref={ref}
className={`h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 ${className}`}
{...props}
/>
))
export const TableHead = forwardRef<HTMLTableHeaderCellElement, React.ThHTMLAttributes<HTMLTableHeaderCellElement>>(
({ className, ...props }, ref) => (
<th
ref={ref}
className={`h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 ${className}`}
{...props}
/>
)
)
TableHead.displayName = "TableHead"

export const TableCell = forwardRef(({ className, ...props }, ref) => (
<td ref={ref} className={`p-2 align-middle [&:has([role=checkbox])]:pr-0 ${className}`} {...props} />
))
export const TableCell = forwardRef<HTMLTableDataCellElement, React.TdHTMLAttributes<HTMLTableDataCellElement>>(
({ className, ...props }, ref) => (
<td ref={ref} className={`p-2 align-middle [&:has([role=checkbox])]:pr-0 ${className}`} {...props} />
)
)
TableCell.displayName = "TableCell"
Loading