refactor: externalize AppPage component and streamline all tool pages
This commit is contained in:
@@ -1,17 +1,13 @@
|
|||||||
import { FigletConverter } from '@/components/figlet/FigletConverter';
|
import { FigletConverter } from '@/components/figlet/FigletConverter';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
|
|
||||||
export default function FigletPage() {
|
export default function FigletPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Figlet ASCII"
|
||||||
<div>
|
description="ASCII Art Text Generator with 373 Fonts"
|
||||||
<h1 className="text-4xl font-bold mb-2">Figlet ASCII</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<FigletConverter />
|
||||||
ASCII Art Text Generator with 373 Fonts
|
</AppPage>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<FigletConverter />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import { FileConverter } from '@/components/media/FileConverter';
|
import { FileConverter } from '@/components/media/FileConverter';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
|
|
||||||
export default function MediaPage() {
|
export default function MediaPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Media Converter"
|
||||||
<div>
|
description="Professional browser-based media conversion for video, audio, and images"
|
||||||
<h1 className="text-4xl font-bold mb-2">Media Converter</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<FileConverter />
|
||||||
Professional browser-based media conversion for video, audio, and images
|
</AppPage>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<FileConverter />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Textarea } from '@/components/ui/textarea';
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { PaletteGrid } from '@/components/pastel/PaletteGrid';
|
import { PaletteGrid } from '@/components/pastel/PaletteGrid';
|
||||||
import { ExportMenu } from '@/components/pastel/ExportMenu';
|
import { ExportMenu } from '@/components/pastel/ExportMenu';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useLighten, useDarken, useSaturate, useDesaturate, useRotate } from '@/lib/pastel/api/queries';
|
import { useLighten, useDarken, useSaturate, useDesaturate, useRotate } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2, Upload, Download } from 'lucide-react';
|
import { Loader2, Upload, Download } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -92,16 +93,11 @@ export default function BatchPage() {
|
|||||||
rotateMutation.isPending;
|
rotateMutation.isPending;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Batch Operations"
|
||||||
<div>
|
description="Process multiple colors at once with manipulation operations"
|
||||||
<h1 className="text-4xl font-bold mb-2">Batch Operations</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Process multiple colors at once with manipulation operations
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Input */}
|
{/* Input */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -213,7 +209,6 @@ export default function BatchPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useSimulateColorBlindness } from '@/lib/pastel/api/queries';
|
import { useSimulateColorBlindness } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2, Eye, Plus, X } from 'lucide-react';
|
import { Loader2, Eye, Plus, X } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -66,16 +67,11 @@ export default function ColorBlindPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Color Blindness Simulator"
|
||||||
<div>
|
description="Simulate how colors appear with different types of color blindness"
|
||||||
<h1 className="text-4xl font-bold mb-2">Color Blindness Simulator</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Simulate how colors appear with different types of color blindness
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Controls */}
|
{/* Controls */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -229,7 +225,6 @@ export default function ColorBlindPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ColorPicker } from '@/components/pastel/ColorPicker';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { getContrastRatio, hexToRgb, checkWCAGCompliance } from '@/lib/pastel/utils/color';
|
import { getContrastRatio, hexToRgb, checkWCAGCompliance } from '@/lib/pastel/utils/color';
|
||||||
import { ArrowLeftRight, Check, X } from 'lucide-react';
|
import { ArrowLeftRight, Check, X } from 'lucide-react';
|
||||||
|
|
||||||
@@ -57,16 +58,11 @@ export default function ContrastPage() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Contrast Checker"
|
||||||
<div>
|
description="Test color combinations for WCAG 2.1 compliance"
|
||||||
<h1 className="text-4xl font-bold mb-2">Contrast Checker</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Test color combinations for WCAG 2.1 compliance
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Color Pickers */}
|
{/* Color Pickers */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -183,7 +179,6 @@ export default function ContrastPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useGenerateDistinct } from '@/lib/pastel/api/queries';
|
import { useGenerateDistinct } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -38,16 +39,11 @@ export default function DistinctPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Distinct Colors Generator"
|
||||||
<div>
|
description="Generate visually distinct colors using simulated annealing"
|
||||||
<h1 className="text-4xl font-bold mb-2">Distinct Colors Generator</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
Generate visually distinct colors using simulated annealing
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
||||||
{/* Controls */}
|
{/* Controls */}
|
||||||
<div className="lg:col-span-1">
|
<div className="lg:col-span-1">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -137,7 +133,6 @@ export default function DistinctPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ExportMenu } from '@/components/pastel/ExportMenu';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useGenerateGradient } from '@/lib/pastel/api/queries';
|
import { useGenerateGradient } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2, Plus, X } from 'lucide-react';
|
import { Loader2, Plus, X } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -48,16 +49,11 @@ export default function GradientPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Gradient Creator"
|
||||||
<div>
|
description="Create smooth color gradients with multiple stops"
|
||||||
<h1 className="text-4xl font-bold mb-2">Gradient Creator</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Create smooth color gradients with multiple stops
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Controls */}
|
{/* Controls */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -169,7 +165,6 @@ export default function GradientPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useGeneratePalette } from '@/lib/pastel/api/queries';
|
import { useGeneratePalette } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2, Palette } from 'lucide-react';
|
import { Loader2, Palette } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -59,16 +60,11 @@ export default function HarmonyPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Harmony Palette Generator"
|
||||||
<div>
|
description="Create color harmonies based on color theory principles"
|
||||||
<h1 className="text-4xl font-bold mb-2">Harmony Palette Generator</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Create color harmonies based on color theory principles
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Controls */}
|
{/* Controls */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -160,7 +156,6 @@ export default function HarmonyPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useNamedColors } from '@/lib/pastel/api/queries';
|
import { useNamedColors } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { parse_color } from '@valknarthing/pastel-wasm';
|
import { parse_color } from '@valknarthing/pastel-wasm';
|
||||||
@@ -44,15 +45,11 @@ export default function NamedColorsPage() {
|
|||||||
}, [data, search, sortBy]);
|
}, [data, search, sortBy]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Named Colors"
|
||||||
<div>
|
description="Explore 148 CSS/X11 named colors"
|
||||||
<h1 className="text-4xl font-bold mb-2">Named Colors</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="space-y-8">
|
||||||
Explore 148 CSS/X11 named colors
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Search and Filters */}
|
{/* Search and Filters */}
|
||||||
<div className="flex flex-col sm:flex-row gap-4">
|
<div className="flex flex-col sm:flex-row gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
@@ -120,6 +117,6 @@ export default function NamedColorsPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ColorDisplay } from '@/components/pastel/ColorDisplay';
|
|||||||
import { ColorInfo } from '@/components/pastel/ColorInfo';
|
import { ColorInfo } from '@/components/pastel/ColorInfo';
|
||||||
import { ManipulationPanel } from '@/components/pastel/ManipulationPanel';
|
import { ManipulationPanel } from '@/components/pastel/ManipulationPanel';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useColorInfo } from '@/lib/pastel/api/queries';
|
import { useColorInfo } from '@/lib/pastel/api/queries';
|
||||||
import { useColorHistory } from '@/lib/pastel/stores/historyStore';
|
import { useColorHistory } from '@/lib/pastel/stores/historyStore';
|
||||||
import { Loader2, Share2, History, X } from 'lucide-react';
|
import { Loader2, Share2, History, X } from 'lucide-react';
|
||||||
@@ -73,16 +74,11 @@ function PlaygroundContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Pastel"
|
||||||
<div>
|
description="Interactive color manipulation and analysis tool"
|
||||||
<h1 className="text-4xl font-bold mb-2">Pastel</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Interactive color manipulation and analysis tool
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Left Column: Color Picker and Display */}
|
{/* Left Column: Color Picker and Display */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -197,8 +193,7 @@ function PlaygroundContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ColorPicker } from '@/components/pastel/ColorPicker';
|
|||||||
import { ColorDisplay } from '@/components/pastel/ColorDisplay';
|
import { ColorDisplay } from '@/components/pastel/ColorDisplay';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
import { useTextColor } from '@/lib/pastel/api/queries';
|
import { useTextColor } from '@/lib/pastel/api/queries';
|
||||||
import { Loader2, Palette, Plus, X, CheckCircle2, XCircle } from 'lucide-react';
|
import { Loader2, Palette, Plus, X, CheckCircle2, XCircle } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -55,16 +56,11 @@ export default function TextColorPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Text Color Optimizer"
|
||||||
<div>
|
description="Automatically find the best text color (black or white) for any background color"
|
||||||
<h1 className="text-4xl font-bold mb-2">Text Color Optimizer</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
Automatically find the best text color (black or white) for any background color
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
||||||
{/* Input */}
|
{/* Input */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
<Card>
|
||||||
@@ -226,7 +222,6 @@ export default function TextColorPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPage>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import MainConverter from '@/components/units/MainConverter';
|
import MainConverter from '@/components/units/MainConverter';
|
||||||
|
import { AppPage } from '@/components/layout/AppPage';
|
||||||
|
|
||||||
export default function UnitsPage() {
|
export default function UnitsPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen py-12">
|
<AppPage
|
||||||
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
title="Units Converter"
|
||||||
<div>
|
description="Smart unit converter with 187 units across 23 categories"
|
||||||
<h1 className="text-4xl font-bold mb-2">Units Converter</h1>
|
>
|
||||||
<p className="text-muted-foreground">
|
<MainConverter />
|
||||||
Smart unit converter with 187 units across 23 categories
|
</AppPage>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<MainConverter />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
29
components/layout/AppPage.tsx
Normal file
29
components/layout/AppPage.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
interface AppPageProps {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AppPage({ title, description, children, className }: AppPageProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn("min-h-screen py-12", className)}>
|
||||||
|
<div className="max-w-7xl mx-auto px-8 space-y-8">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-4xl font-bold mb-2">{title}</h1>
|
||||||
|
{description && (
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user