refactor(PBI-77): standaardiseer loading-skeletons rond shadcn Skeleton (#186)
* refactor(PBI-77): align TaskDialogSkeleton with entity-dialog-layout (T-899) Use entityDialogContentClasses/HeaderClasses/BodyClasses/FooterClasses from components/shared/entity-dialog-layout.ts instead of hand-copied class strings. Header, body, and footer skeletons now mimic the actual TaskDialog form fields (code, title, description, plan, priority, status, delete + cancel/save buttons). Fixes mobile layout-shift: the shared content-classes include max-sm: fullscreen modifiers that the previous hand-rolled subset missed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(PBI-77): extract BacklogPageSkeleton from 3 identical loadings (T-900) The loading.tsx files for products/[id], sprint/[sprintId], and sprint/[sprintId]/planning were 100% identical hand-rolled animate-pulse markup. Extract the shape into components/loading/backlog-page-skeleton.tsx using the shadcn <Skeleton> component, and re-export it from the three loading files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(PBI-77): dashboard + settings loading on shadcn Skeleton (T-901) Replace hand-rolled animate-pulse + bg-border divs with the shadcn <Skeleton> component for consistent bg-muted color and pulse timing across all five loading.tsx files. No layout-shift: heights, widths, and rounded radii are preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a0e5867857
commit
f8693d126b
7 changed files with 92 additions and 127 deletions
|
|
@ -1,10 +1,12 @@
|
||||||
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<div className="p-6 max-w-4xl mx-auto w-full animate-pulse">
|
<div className="p-6 max-w-4xl mx-auto w-full">
|
||||||
<div className="h-6 w-32 bg-border rounded mb-6" />
|
<Skeleton className="h-6 w-32 mb-6" />
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
{[1, 2, 3].map(i => (
|
{[1, 2, 3].map((i) => (
|
||||||
<div key={i} className="h-20 bg-border/50 rounded-xl" />
|
<Skeleton key={i} className="h-20 rounded-xl" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1 @@
|
||||||
export default function Loading() {
|
export { default } from '@/components/loading/backlog-page-skeleton'
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full animate-pulse">
|
|
||||||
{/* Header skeleton */}
|
|
||||||
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0 flex items-center justify-between">
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<div className="h-4 w-32 bg-border rounded" />
|
|
||||||
<div className="h-3 w-48 bg-border/60 rounded" />
|
|
||||||
</div>
|
|
||||||
<div className="h-7 w-24 bg-border rounded" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Split pane skeleton */}
|
|
||||||
<div className="flex-1 flex overflow-hidden">
|
|
||||||
{/* Left */}
|
|
||||||
<div className="w-2/5 border-r border-border p-4 space-y-3">
|
|
||||||
<div className="h-4 w-24 bg-border rounded" />
|
|
||||||
{[1, 2, 3, 4, 5].map(i => (
|
|
||||||
<div key={i} className="h-8 bg-border/50 rounded" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{/* Right */}
|
|
||||||
<div className="flex-1 p-4 space-y-3">
|
|
||||||
<div className="h-4 w-16 bg-border rounded" />
|
|
||||||
<div className="flex gap-2 flex-wrap">
|
|
||||||
{[1, 2, 3].map(i => (
|
|
||||||
<div key={i} className="w-28 h-24 bg-border/50 rounded-lg" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1 @@
|
||||||
export default function Loading() {
|
export { default } from '@/components/loading/backlog-page-skeleton'
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full animate-pulse">
|
|
||||||
{/* Header skeleton */}
|
|
||||||
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0 flex items-center justify-between">
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<div className="h-4 w-32 bg-border rounded" />
|
|
||||||
<div className="h-3 w-48 bg-border/60 rounded" />
|
|
||||||
</div>
|
|
||||||
<div className="h-7 w-24 bg-border rounded" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Split pane skeleton */}
|
|
||||||
<div className="flex-1 flex overflow-hidden">
|
|
||||||
{/* Left */}
|
|
||||||
<div className="w-2/5 border-r border-border p-4 space-y-3">
|
|
||||||
<div className="h-4 w-24 bg-border rounded" />
|
|
||||||
{[1, 2, 3, 4, 5].map(i => (
|
|
||||||
<div key={i} className="h-8 bg-border/50 rounded" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{/* Right */}
|
|
||||||
<div className="flex-1 p-4 space-y-3">
|
|
||||||
<div className="h-4 w-16 bg-border rounded" />
|
|
||||||
<div className="flex gap-2 flex-wrap">
|
|
||||||
{[1, 2, 3].map(i => (
|
|
||||||
<div key={i} className="w-28 h-24 bg-border/50 rounded-lg" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1 @@
|
||||||
export default function Loading() {
|
export { default } from '@/components/loading/backlog-page-skeleton'
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full animate-pulse">
|
|
||||||
{/* Header skeleton */}
|
|
||||||
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0 flex items-center justify-between">
|
|
||||||
<div className="space-y-1.5">
|
|
||||||
<div className="h-4 w-32 bg-border rounded" />
|
|
||||||
<div className="h-3 w-48 bg-border/60 rounded" />
|
|
||||||
</div>
|
|
||||||
<div className="h-7 w-24 bg-border rounded" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Split pane skeleton */}
|
|
||||||
<div className="flex-1 flex overflow-hidden">
|
|
||||||
{/* Left */}
|
|
||||||
<div className="w-2/5 border-r border-border p-4 space-y-3">
|
|
||||||
<div className="h-4 w-24 bg-border rounded" />
|
|
||||||
{[1, 2, 3, 4, 5].map(i => (
|
|
||||||
<div key={i} className="h-8 bg-border/50 rounded" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{/* Right */}
|
|
||||||
<div className="flex-1 p-4 space-y-3">
|
|
||||||
<div className="h-4 w-16 bg-border rounded" />
|
|
||||||
<div className="flex gap-2 flex-wrap">
|
|
||||||
{[1, 2, 3].map(i => (
|
|
||||||
<div key={i} className="w-28 h-24 bg-border/50 rounded-lg" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<div className="p-6 max-w-2xl mx-auto w-full space-y-4 animate-pulse">
|
<div className="p-6 max-w-2xl mx-auto w-full space-y-4">
|
||||||
<div className="h-6 w-28 bg-border rounded" />
|
<Skeleton className="h-6 w-28" />
|
||||||
{[1, 2, 3, 4].map(i => (
|
{[1, 2, 3, 4].map((i) => (
|
||||||
<div key={i} className="h-28 bg-border/50 rounded-xl" />
|
<Skeleton key={i} className="h-28 rounded-xl" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
|
||||||
|
import {
|
||||||
|
entityDialogBodyClasses,
|
||||||
|
entityDialogContentClasses,
|
||||||
|
entityDialogFooterClasses,
|
||||||
|
entityDialogHeaderClasses,
|
||||||
|
} from '@/components/shared/entity-dialog-layout'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
export function TaskDialogSkeleton() {
|
export function TaskDialogSkeleton() {
|
||||||
|
|
@ -8,32 +14,54 @@ export function TaskDialogSkeleton() {
|
||||||
<DialogContent
|
<DialogContent
|
||||||
showCloseButton={false}
|
showCloseButton={false}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-col p-0 gap-0',
|
entityDialogContentClasses,
|
||||||
'max-h-[90vh] w-full max-w-[calc(100%-2rem)]',
|
|
||||||
'sm:max-w-[90vw] sm:max-h-[85vh]',
|
|
||||||
'lg:max-w-[50vw] lg:min-w-[480px]',
|
|
||||||
'[animation-delay:200ms] [animation-fill-mode:backwards]',
|
'[animation-delay:200ms] [animation-fill-mode:backwards]',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<DialogTitle className="sr-only">Taak laden…</DialogTitle>
|
<DialogTitle className="sr-only">Taak laden…</DialogTitle>
|
||||||
|
|
||||||
{/* Header */}
|
<div className={entityDialogHeaderClasses}>
|
||||||
<div className="px-6 pt-5 pb-4 border-b border-outline-variant shrink-0">
|
<div className="flex items-center gap-2">
|
||||||
<Skeleton className="h-7 w-40" />
|
<Skeleton className="h-7 w-40" />
|
||||||
|
<Skeleton className="h-5 w-14 rounded-full" />
|
||||||
|
</div>
|
||||||
|
<Skeleton className="h-4 w-28" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Body — 3 bars mimicking title + description + plan */}
|
<div className={entityDialogBodyClasses}>
|
||||||
<div className="flex-1 px-6 py-6 space-y-6">
|
<div>
|
||||||
<Skeleton className="h-14 w-full" />
|
<Skeleton className="h-4 w-12 mb-2" />
|
||||||
<Skeleton className="h-24 w-full" />
|
<Skeleton className="h-9 w-full" />
|
||||||
<Skeleton className="h-32 w-full" />
|
</div>
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-4 w-12 mb-2" />
|
||||||
|
<Skeleton className="h-14 w-full" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-4 w-24 mb-2" />
|
||||||
|
<Skeleton className="h-20 w-full" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-4 w-32 mb-2" />
|
||||||
|
<Skeleton className="h-32 w-full" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-4 w-16 mb-2" />
|
||||||
|
<Skeleton className="h-9 w-64" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-4 w-12 mb-2" />
|
||||||
|
<Skeleton className="h-9 w-48" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
<div className={entityDialogFooterClasses}>
|
||||||
<div className="border-t border-outline-variant px-6 py-4 shrink-0">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex justify-end gap-2">
|
<Skeleton className="h-9 w-28" />
|
||||||
<Skeleton className="h-8 w-24" />
|
<div className="flex gap-2">
|
||||||
<Skeleton className="h-8 w-24" />
|
<Skeleton className="h-9 w-24" />
|
||||||
|
<Skeleton className="h-9 w-24" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
32
components/loading/backlog-page-skeleton.tsx
Normal file
32
components/loading/backlog-page-skeleton.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
|
|
||||||
|
export default function BacklogPageSkeleton() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0 flex items-center justify-between">
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Skeleton className="h-4 w-32" />
|
||||||
|
<Skeleton className="h-3 w-48" />
|
||||||
|
</div>
|
||||||
|
<Skeleton className="h-7 w-24" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 flex overflow-hidden">
|
||||||
|
<div className="w-2/5 border-r border-border p-4 space-y-3">
|
||||||
|
<Skeleton className="h-4 w-24" />
|
||||||
|
{[1, 2, 3, 4, 5].map((i) => (
|
||||||
|
<Skeleton key={i} className="h-8 w-full" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 p-4 space-y-3">
|
||||||
|
<Skeleton className="h-4 w-16" />
|
||||||
|
<div className="flex gap-2 flex-wrap">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<Skeleton key={i} className="w-28 h-24 rounded-lg" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue