Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
47681f7
과제를 위한 빈 커밋 날리기
Leehyunji0715 Dec 9, 2025
b5fada1
feat: add 'post' and 'user' entity. API and Type
Leehyunji0715 Dec 9, 2025
e80e744
fix: update posts/users api/model with DTO
Leehyunji0715 Dec 9, 2025
3a4ed59
코드커밋: apply fetching posts and users
Leehyunji0715 Dec 9, 2025
accfaf2
feat: add React19 component types
Leehyunji0715 Dec 11, 2025
f2301fc
feat: add queryClient
Leehyunji0715 Dec 11, 2025
f6a9ca6
chore: add tanstack query
Leehyunji0715 Dec 11, 2025
5a10af8
feat: TanStack Query 설정 및 UserDTO 타입 개선
Leehyunji0715 Dec 11, 2025
373e65b
refactor: FSD 아키텍처 적용 및 컴포넌트 세분화
Leehyunji0715 Dec 11, 2025
b77c142
feat: aPP
Leehyunji0715 Dec 11, 2025
640be4d
feat: FSD entity - post, user query/requests
Leehyunji0715 Dec 11, 2025
a35a5c5
feat: FSD features post ui
Leehyunji0715 Dec 11, 2025
52b2df7
feat: shared
Leehyunji0715 Dec 11, 2025
a598b2b
feat: FSD page
Leehyunji0715 Dec 11, 2025
8934ae1
feat: add post table widget
Leehyunji0715 Dec 12, 2025
82ca877
feat: add hook for getting post query params
Leehyunji0715 Dec 12, 2025
b584088
reafactor: remove unused image
Leehyunji0715 Dec 12, 2025
b522690
feat: post tanstack query
Leehyunji0715 Dec 12, 2025
bcedcd6
feat: user feature detail diaglog
Leehyunji0715 Dec 12, 2025
7bc5625
feat: apply 'useLocation', 'useNavigation' for url change
Leehyunji0715 Dec 12, 2025
b52ea1c
feat: User Detail Modal and apply useUserQuery
Leehyunji0715 Dec 12, 2025
4d2a16c
refactor: remove unused hook properties and add post detail dialog
Leehyunji0715 Dec 12, 2025
f7543ee
feat: set search input uncrolled
Leehyunji0715 Dec 12, 2025
ff15cdc
feat: update fetch post api
Leehyunji0715 Dec 12, 2025
cae000d
refactor: add HighlightText shared component
Leehyunji0715 Dec 12, 2025
00ab723
feat: Query Dev Tools
Leehyunji0715 Dec 12, 2025
6a0f9a6
fix: set default query search value
Leehyunji0715 Dec 12, 2025
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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"coverage": "vitest run --coverage"
},
"dependencies": {
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
"react": "^19.2.1",
"react-dom": "^19.2.1"
},
Expand Down
1,746 changes: 705 additions & 1,041 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

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

export const App = () => {
return (
<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>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
1 change: 0 additions & 1 deletion src/assets/react.svg

This file was deleted.

189 changes: 130 additions & 59 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from "react"
import { forwardRef } from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { Check, ChevronDown, X } from "lucide-react"
Expand Down Expand Up @@ -35,14 +34,23 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, Var
className?: string
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, ...props }, ref) => {
export const Button = ({
className,
variant,
size,
ref,
...props
}: ButtonProps & { ref?: React.Ref<HTMLButtonElement> }) => {
return <button className={buttonVariants({ variant, size, className })} ref={ref} {...props} />
})

Button.displayName = "Button"
}

// 입력 컴포넌트
export const Input = forwardRef(({ className, type, ...props }, ref) => {
export const Input = ({
className,
type,
ref,
...props
}: React.InputHTMLAttributes<HTMLInputElement> & { ref?: React.Ref<HTMLInputElement> }) => {
return (
<input
type={type}
Expand All @@ -51,48 +59,69 @@ export const Input = forwardRef(({ className, type, ...props }, ref) => {
{...props}
/>
)
})
Input.displayName = "Input"
}

// 카드 컴포넌트
export const Card = forwardRef(({ className, ...props }, ref) => (
export const Card = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) => (
<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) => (
export const CardHeader = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) => (
<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) => (
export const CardTitle = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLHeadingElement> & { ref?: React.Ref<HTMLHeadingElement> }) => (
<h3 ref={ref} className={`text-2xl font-semibold leading-none tracking-tight ${className}`} {...props} />
))
CardTitle.displayName = "CardTitle"
)

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

// 텍스트 영역 컴포넌트
export const Textarea = forwardRef(({ className, ...props }, ref) => {
export const Textarea = ({
className,
ref,
...props
}: React.TextareaHTMLAttributes<HTMLTextAreaElement> & { ref?: React.Ref<HTMLTextAreaElement> }) => {
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 = ({
className,
children,
ref,
...props
}: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Trigger>>
}) => (
<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 @@ -101,10 +130,17 @@ export const SelectTrigger = forwardRef(({ className, children, ...props }, ref)
{children}
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
)

export const SelectContent = forwardRef(({ className, children, position = "popper", ...props }, ref) => (
export const SelectContent = ({
className,
children,
position = "popper" as const,
ref,
...props
}: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> & {
ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Content>>
}) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
Expand All @@ -115,10 +151,16 @@ export const SelectContent = forwardRef(({ className, children, position = "popp
<SelectPrimitive.Viewport className="p-1">{children}</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
)

export const SelectItem = forwardRef(({ className, children, ...props }, ref) => (
export const SelectItem = ({
className,
children,
ref,
...props
}: React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> & {
ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Item>>
}) => (
<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 @@ -131,16 +173,20 @@ export const SelectItem = forwardRef(({ className, children, ...props }, ref) =>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
)

// 대화상자 컴포넌트
export const Dialog = DialogPrimitive.Root
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 = ({
className,
children,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) => (
<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 @@ -155,60 +201,85 @@ export const DialogContent = forwardRef(({ className, children, ...props }, ref)
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
)

export const DialogHeader = ({ className, ...props }) => (
<div className={`flex flex-col space-y-1.5 text-center sm:text-left ${className}`} {...props} />
export const DialogHeader = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) => (
<div ref={ref} 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 = ({
className,
ref,
...props
}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> & {
ref?: React.Ref<React.ElementRef<typeof DialogPrimitive.Title>>
}) => (
<DialogPrimitive.Title
ref={ref}
className={`text-lg font-semibold leading-none tracking-tight ${className}`}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
)

// 테이블 컴포넌트
export const Table = forwardRef(({ className, ...props }, ref) => (
export const Table = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLTableElement> & { ref?: React.Ref<HTMLTableElement> }) => (
<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) => (
export const TableHeader = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLTableSectionElement> & { ref?: React.Ref<HTMLTableSectionElement> }) => (
<thead ref={ref} className={`[&_tr]:border-b ${className}`} {...props} />
))
TableHeader.displayName = "TableHeader"
)

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

export const TableRow = forwardRef(({ className, ...props }, ref) => (
export const TableRow = ({
className,
ref,
...props
}: React.HTMLAttributes<HTMLTableRowElement> & { ref?: React.Ref<HTMLTableRowElement> }) => (
<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) => (
export const TableHead = ({
className,
ref,
...props
}: React.ThHTMLAttributes<HTMLTableCellElement> & { ref?: React.Ref<HTMLTableCellElement> }) => (
<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) => (
export const TableCell = ({
className,
ref,
...props
}: React.TdHTMLAttributes<HTMLTableCellElement> & { ref?: React.Ref<HTMLTableCellElement> }) => (
<td ref={ref} className={`p-2 align-middle [&:has([role=checkbox])]:pr-0 ${className}`} {...props} />
))
TableCell.displayName = "TableCell"
)
Loading