Refactor: Implement desktop layout modernization

This commit is contained in:
gpt-engineer-app[bot]
2025-10-14 20:16:24 +00:00
parent 2eec20f653
commit b8f2889b1d
6 changed files with 387 additions and 302 deletions

View File

@@ -244,7 +244,7 @@ export function AccountProfileTab() {
const isDeactivated = profile?.deactivated || false;
return (
<div className="space-y-8">
<div className="space-y-6">
{/* Deletion Status Banner */}
{deletionRequest && (
<DeletionStatusBanner
@@ -253,26 +253,93 @@ export function AccountProfileTab() {
/>
)}
{/* Profile Picture */}
<div className="space-y-4">
<h3 className="text-lg font-medium">Profile Picture</h3>
<PhotoUpload
variant="avatar"
maxFiles={1}
maxSizeMB={1}
existingPhotos={avatarUrl ? [avatarUrl] : []}
onUploadComplete={handleAvatarUpload}
currentImageId={avatarImageId}
onError={(error) => {
handleError(new Error(error), { action: 'Upload avatar' });
}}
/>
{/* Profile Picture + Account Info Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Profile Picture */}
<Card>
<CardHeader>
<CardTitle>Profile Picture</CardTitle>
<CardDescription>Upload your profile picture</CardDescription>
</CardHeader>
<CardContent>
<PhotoUpload
variant="avatar"
maxFiles={1}
maxSizeMB={1}
existingPhotos={avatarUrl ? [avatarUrl] : []}
onUploadComplete={handleAvatarUpload}
currentImageId={avatarImageId}
onError={(error) => {
handleError(new Error(error), { action: 'Upload avatar' });
}}
/>
</CardContent>
</Card>
{/* Account Information */}
<Card>
<CardHeader>
<CardTitle>Account Information</CardTitle>
<CardDescription>View your account details</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{pendingEmail && (
<EmailChangeStatus
currentEmail={user?.email || ''}
pendingEmail={pendingEmail}
onCancel={() => setShowCancelEmailDialog(true)}
/>
)}
<div className="p-4 bg-muted/50 rounded-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex-1">
<p className="text-sm font-medium">Email Address</p>
<div className="flex items-center gap-2 mt-1">
<p className="text-sm text-muted-foreground">{user?.email}</p>
{pendingEmail ? (
<Badge variant="secondary" className="bg-blue-500/10 text-blue-500 border-blue-500/20 text-xs">
Change Pending
</Badge>
) : user?.email_confirmed_at ? (
<Badge variant="secondary" className="text-xs">Verified</Badge>
) : (
<Badge variant="outline" className="text-xs">Pending Verification</Badge>
)}
</div>
</div>
<Button
variant="outline"
size="sm"
onClick={() => setShowEmailDialog(true)}
disabled={!!pendingEmail}
>
<Mail className="w-4 h-4 mr-2" />
Change
</Button>
</div>
<Separator />
<div>
<p className="text-sm font-medium">Account Created</p>
<p className="text-sm text-muted-foreground mt-1">
{profile?.created_at ? new Date(profile.created_at).toLocaleDateString() : 'N/A'}
</p>
</div>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Profile Information */}
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Profile Information</CardTitle>
<CardDescription>Update your public profile details</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<h3 className="text-lg font-medium">Profile Information</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
@@ -393,86 +460,57 @@ export function AccountProfileTab() {
)}
</div>
<div className="flex items-center justify-between">
<Button
type="submit"
disabled={
loading ||
isDeactivated ||
isSaving ||
usernameValidation.isChecking ||
usernameValidation.isAvailable === false
}
>
{loading || isSaving ? 'Saving...' : 'Save Changes'}
</Button>
{lastSaved && !loading && !isSaving && (
<span className="text-sm text-muted-foreground">
Last saved {formatDistanceToNow(lastSaved, { addSuffix: true })}
</span>
)}
</div>
{isDeactivated && (
<p className="text-sm text-muted-foreground">
Your account is deactivated. Profile editing is disabled.
</p>
)}
</form>
<Separator />
{/* Account Information */}
<div className="space-y-4">
<h3 className="text-lg font-medium">Account Information</h3>
<div className="space-y-4">
{pendingEmail && (
<EmailChangeStatus
currentEmail={user?.email || ''}
pendingEmail={pendingEmail}
onCancel={() => setShowCancelEmailDialog(true)}
/>
)}
<div className="p-4 bg-muted/50 rounded-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex-1">
<p className="text-sm font-medium">Email Address</p>
<div className="flex items-center gap-2 mt-1">
<p className="text-sm text-muted-foreground">{user?.email}</p>
{pendingEmail ? (
<Badge variant="secondary" className="bg-blue-500/10 text-blue-500 border-blue-500/20 text-xs">
Change Pending
</Badge>
) : user?.email_confirmed_at ? (
<Badge variant="secondary" className="text-xs">Verified</Badge>
) : (
<Badge variant="outline" className="text-xs">Pending Verification</Badge>
)}
</div>
</div>
<Button
variant="outline"
size="sm"
onClick={() => setShowEmailDialog(true)}
disabled={!!pendingEmail}
type="submit"
disabled={
loading ||
isDeactivated ||
isSaving ||
usernameValidation.isChecking ||
usernameValidation.isAvailable === false
}
>
<Mail className="w-4 h-4 mr-2" />
Change Email
{loading || isSaving ? 'Saving...' : 'Save Changes'}
</Button>
{lastSaved && !loading && !isSaving && (
<span className="text-sm text-muted-foreground">
Last saved {formatDistanceToNow(lastSaved, { addSuffix: true })}
</span>
)}
</div>
<Separator />
<div>
<p className="text-sm font-medium">Account Created</p>
<p className="text-sm text-muted-foreground mt-1">
{profile?.created_at ? new Date(profile.created_at).toLocaleDateString() : 'N/A'}
{isDeactivated && (
<p className="text-sm text-muted-foreground">
Your account is deactivated. Profile editing is disabled.
</p>
</div>
</div>
</div>
</div>
)}
</form>
</CardContent>
</Card>
{/* Danger Zone */}
<Card className="border-destructive/50">
<CardHeader>
<CardTitle className="text-destructive">Danger Zone</CardTitle>
<CardDescription>Irreversible account actions</CardDescription>
</CardHeader>
<CardContent>
<Button
variant="destructive"
onClick={() => setShowDeletionDialog(true)}
disabled={!!deletionRequest}
>
<Trash2 className="w-4 h-4 mr-2" />
Delete Account
</Button>
{deletionRequest && (
<p className="text-sm text-muted-foreground mt-2">
Account deletion already in progress
</p>
)}
</CardContent>
</Card>
{/* Email Change Dialog */}
{user && (
@@ -486,29 +524,6 @@ export function AccountProfileTab() {
{/* Cancel Email Change Dialog */}
<AlertDialog open={showCancelEmailDialog} onOpenChange={setShowCancelEmailDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Cancel Email Change?</AlertDialogTitle>
<AlertDialogDescription>
This will cancel your pending email change to <strong>{pendingEmail}</strong>.
Your email will remain as <strong>{user?.email}</strong>.
You can start a new email change request at any time.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={cancellingEmail}>Keep Change</AlertDialogCancel>
<AlertDialogAction
onClick={handleCancelEmailChange}
disabled={cancellingEmail}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{cancellingEmail ? 'Cancelling...' : 'Yes, Cancel Change'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<Separator />
{/* Danger Zone */}
<Card className="border-destructive">
@@ -538,6 +553,28 @@ export function AccountProfileTab() {
</CardContent>
</Card>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Cancel Email Change?</AlertDialogTitle>
<AlertDialogDescription>
This will cancel your pending email change to <strong>{pendingEmail}</strong>.
Your email will remain as <strong>{user?.email}</strong>.
You can start a new email change request at any time.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={cancellingEmail}>Keep Change</AlertDialogCancel>
<AlertDialogAction
onClick={handleCancelEmailChange}
disabled={cancellingEmail}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{cancellingEmail ? 'Cancelling...' : 'Yes, Cancel Change'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{/* Account Deletion Dialog */}
<AccountDeletionDialog
open={showDeletionDialog}

View File

@@ -281,16 +281,16 @@ export function DataExportTab() {
}
return (
<div className="space-y-8">
{/* Personal Statistics */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<BarChart3 className="w-5 h-5" />
<h3 className="text-lg font-medium">Personal Statistics</h3>
</div>
<div className="space-y-6">
{/* Statistics + Recent Activity Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Personal Statistics */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<BarChart3 className="w-5 h-5" />
<CardTitle>Personal Statistics</CardTitle>
</div>
<CardDescription>
Your activity and contribution statistics on ThrillWiki
</CardDescription>
@@ -334,22 +334,16 @@ export function DataExportTab() {
)}
</CardContent>
</Card>
</div>
<Separator />
{/* Export Your Data */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Download className="w-5 h-5" />
<h3 className="text-lg font-medium">Export Your Data</h3>
</div>
{/* Account Activity */}
<Card>
<CardHeader>
<CardTitle>Download Your Data</CardTitle>
<div className="flex items-center gap-2">
<Activity className="w-5 h-5" />
<CardTitle>Account Activity</CardTitle>
</div>
<CardDescription>
Export all your ThrillWiki data in JSON format. This includes your profile, reviews, lists, and activity history.
Recent account activity and changes
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
@@ -437,24 +431,6 @@ export function DataExportTab() {
{exporting ? 'Exporting Data...' : 'Export My Data'}
</Button>
</CardContent>
</Card>
</div>
<Separator />
{/* Account Activity */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Activity className="w-5 h-5" />
<h3 className="text-lg font-medium">Account Activity</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Recent account activity and changes
</CardDescription>
</CardHeader>
<CardContent>
{recentActivity.length === 0 ? (
<p className="text-sm text-muted-foreground text-center py-4">
@@ -480,6 +456,104 @@ export function DataExportTab() {
</CardContent>
</Card>
</div>
{/* Export Your Data - Full Width */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Download className="w-5 h-5" />
<CardTitle>Export Your Data</CardTitle>
</div>
<CardDescription>
Export all your ThrillWiki data in JSON format. This includes your profile, reviews, lists, and activity history.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{rateLimited && nextAvailableAt && (
<div className="flex items-start gap-3 p-4 border border-yellow-500/20 bg-yellow-500/10 rounded-lg">
<Clock className="w-5 h-5 text-yellow-500 mt-0.5" />
<div className="space-y-1">
<p className="text-sm font-medium">Rate Limited</p>
<p className="text-sm text-muted-foreground">
You can export your data once per hour. Next export available at{' '}
{formatDate(nextAvailableAt)}.
</p>
</div>
</div>
)}
<div className="space-y-4">
<p className="text-sm text-muted-foreground">
Choose what to include in your export:
</p>
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label htmlFor="include_reviews">Include Reviews</Label>
<Switch
id="include_reviews"
checked={exportOptions.include_reviews}
onCheckedChange={(checked) =>
setExportOptions({ ...exportOptions, include_reviews: checked })
}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="include_lists">Include Lists</Label>
<Switch
id="include_lists"
checked={exportOptions.include_lists}
onCheckedChange={(checked) =>
setExportOptions({ ...exportOptions, include_lists: checked })
}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="include_activity_log">Include Activity Log</Label>
<Switch
id="include_activity_log"
checked={exportOptions.include_activity_log}
onCheckedChange={(checked) =>
setExportOptions({ ...exportOptions, include_activity_log: checked })
}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="include_preferences">Include Preferences</Label>
<Switch
id="include_preferences"
checked={exportOptions.include_preferences}
onCheckedChange={(checked) =>
setExportOptions({ ...exportOptions, include_preferences: checked })
}
/>
</div>
</div>
</div>
<div className="flex items-start gap-3 p-4 border rounded-lg">
<AlertCircle className="w-5 h-5 text-blue-500 mt-0.5" />
<div className="space-y-1">
<p className="text-sm font-medium">GDPR Compliance</p>
<p className="text-sm text-muted-foreground">
This export includes all personal data we store about you. You can use this for backup purposes or to migrate to another service.
</p>
</div>
</div>
<Button
onClick={handleDataExport}
disabled={exporting || rateLimited}
className="w-full"
>
<Download className="w-4 h-4 mr-2" />
{exporting ? 'Exporting Data...' : 'Export My Data'}
</Button>
</CardContent>
</Card>
</div>
);
}

View File

@@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Card, CardContent, CardDescription, CardHeader } from '@/components/ui/card';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import { Switch } from '@/components/ui/switch';
import { Skeleton } from '@/components/ui/skeleton';
@@ -304,16 +304,17 @@ export function LocationTab() {
}
return (
<div className="space-y-8">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<div className="space-y-4">
<div className="flex items-center gap-2">
<MapPin className="w-5 h-5" />
<h3 className="text-lg font-medium">Location Settings</h3>
</div>
<div className="space-y-6">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
{/* Location Settings + Personal Information Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Location Settings */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<MapPin className="w-5 h-5" />
<CardTitle>Location Settings</CardTitle>
</div>
<CardDescription>
Set your location for better personalized content and timezone display.
</CardDescription>
@@ -425,18 +426,14 @@ export function LocationTab() {
</div>
</CardContent>
</Card>
</div>
<Separator />
<div className="space-y-4">
<div className="flex items-center gap-2">
<Calendar className="w-5 h-5" />
<h3 className="text-lg font-medium">Personal Information</h3>
</div>
{/* Personal Information */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Calendar className="w-5 h-5" />
<CardTitle>Personal Information</CardTitle>
</div>
<CardDescription>
Optional personal information that can be displayed on your profile.
</CardDescription>
@@ -462,16 +459,15 @@ export function LocationTab() {
</Card>
</div>
<Separator />
<div className="space-y-4">
<div className="flex items-center gap-2">
<Ruler className="w-5 h-5" />
<h3 className="text-lg font-medium">Units & Measurements</h3>
</div>
{/* Unit Preferences + Accessibility Options Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Unit Preferences */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Ruler className="w-5 h-5" />
<CardTitle>Units & Measurements</CardTitle>
</div>
<CardDescription>
Choose your preferred measurement system for displaying distances, speeds, and other measurements.
</CardDescription>
@@ -499,18 +495,14 @@ export function LocationTab() {
</div>
</CardContent>
</Card>
</div>
<Separator />
<div className="space-y-4">
<div className="flex items-center gap-2">
<Accessibility className="w-5 h-5" />
<h3 className="text-lg font-medium">Accessibility Options</h3>
</div>
{/* Accessibility Options */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Accessibility className="w-5 h-5" />
<CardTitle>Accessibility Options</CardTitle>
</div>
<CardDescription>
Customize the interface to meet your accessibility needs.
</CardDescription>
@@ -564,6 +556,7 @@ export function LocationTab() {
</Card>
</div>
{/* Save Button */}
<div className="flex justify-end">
<Button type="submit" disabled={saving}>
{saving ? 'Saving...' : 'Save Settings'}

View File

@@ -239,13 +239,16 @@ export function NotificationsTab() {
</Card>
)}
<Card>
<CardHeader>
<CardTitle>Notification Channels</CardTitle>
<CardDescription>
Choose which channels you'd like to receive notifications through
</CardDescription>
</CardHeader>
{/* Notification Channels + Frequency Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Notification Channels */}
<Card>
<CardHeader>
<CardTitle>Notification Channels</CardTitle>
<CardDescription>
Choose which channels you'd like to receive notifications through
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-0.5">
@@ -298,16 +301,17 @@ export function NotificationsTab() {
}}
/>
</div>
</CardContent>
</Card>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Notification Frequency</CardTitle>
<CardDescription>
Control how often you receive notifications
</CardDescription>
</CardHeader>
{/* Notification Frequency */}
<Card>
<CardHeader>
<CardTitle>Notification Frequency</CardTitle>
<CardDescription>
Control how often you receive notifications
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>Digest Frequency</Label>
@@ -357,9 +361,11 @@ export function NotificationsTab() {
Limit the number of notifications you receive per hour
</p>
</div>
</CardContent>
</Card>
</CardContent>
</Card>
</div>
{/* Workflow Preferences - Full Width */}
{Object.keys(groupedTemplates).map((category) => (
<Card key={category}>
<CardHeader>

View File

@@ -234,17 +234,17 @@ export function PrivacyTab() {
};
return (
<div className="space-y-8">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{/* Profile Visibility */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Eye className="w-5 h-5" />
<h3 className="text-lg font-medium">Profile Visibility</h3>
</div>
<div className="space-y-6">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
{/* Profile Visibility + Activity & Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Profile Visibility */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Eye className="w-5 h-5" />
<CardTitle>Profile Visibility</CardTitle>
</div>
<CardDescription>
Control who can see your profile and personal information.
</CardDescription>
@@ -363,19 +363,14 @@ export function PrivacyTab() {
</CardContent>
</Card>
</div>
<Separator />
{/* Activity & Content */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Shield className="w-5 h-5" />
<h3 className="text-lg font-medium">Activity & Content</h3>
</div>
{/* Activity & Content */}
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Shield className="w-5 h-5" />
<CardTitle>Activity & Content</CardTitle>
</div>
<CardDescription>
Control the visibility of your activities and content.
</CardDescription>
@@ -407,21 +402,17 @@ export function PrivacyTab() {
</Card>
</div>
<Separator />
{/* Search & Discovery */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Search className="w-5 h-5" />
<h3 className="text-lg font-medium">Search & Discovery</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Control how others can find and discover your profile.
</CardDescription>
</CardHeader>
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Search className="w-5 h-5" />
<CardTitle>Search & Discovery</CardTitle>
</div>
<CardDescription>
Control how others can find and discover your profile.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
@@ -437,28 +428,22 @@ export function PrivacyTab() {
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Blocked Users */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<UserX className="w-5 h-5" />
<h3 className="text-lg font-medium">Blocked Users</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Manage users you have blocked from interacting with you.
</CardDescription>
</CardHeader>
<CardContent>
<BlockedUsers />
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<UserX className="w-5 h-5" />
<CardTitle>Blocked Users</CardTitle>
</div>
<CardDescription>
Manage users you have blocked from interacting with you.
</CardDescription>
</CardHeader>
<CardContent>
<BlockedUsers />
</CardContent>
</Card>
{/* Save Button */}
<div className="flex justify-end">

View File

@@ -83,58 +83,48 @@ export default function UserSettings() {
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 py-8">
<div className="max-w-6xl mx-auto">
{/* Header */}
<div className="flex items-center gap-3 mb-8">
<Settings className="w-8 h-8 text-primary" />
<div>
<h1 className="text-3xl font-bold">Settings</h1>
<p className="text-muted-foreground">Manage your account preferences and privacy settings</p>
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="flex items-center gap-3 mb-8">
<Settings className="w-8 h-8 text-primary" />
<div>
<h1 className="text-3xl font-bold">Settings</h1>
<p className="text-muted-foreground">Manage your account preferences and privacy settings</p>
</div>
</div>
</div>
{/* Settings Tabs */}
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<TabsList className="grid w-full grid-cols-2 lg:grid-cols-6 h-auto p-1">
{/* Settings Tabs */}
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<TabsList className="grid w-full grid-cols-2 lg:grid-cols-6 h-auto p-1">
{tabs.map((tab) => {
const Icon = tab.icon;
return (
<TabsTrigger
key={tab.id}
value={tab.id}
className="flex flex-col sm:flex-row items-center gap-2 h-auto py-3 px-2 text-xs sm:text-sm"
>
<Icon className="w-4 h-4" />
<span className="hidden sm:inline lg:inline">{tab.label}</span>
<span className="sm:hidden lg:hidden">{tab.label.split(' ')[0]}</span>
</TabsTrigger>
);
})}
</TabsList>
{tabs.map((tab) => {
const Icon = tab.icon;
const Component = tab.component;
return (
<TabsTrigger
<TabsContent
key={tab.id}
value={tab.id}
className="flex flex-col sm:flex-row items-center gap-2 h-auto py-3 px-2 text-xs sm:text-sm"
className="focus-visible:outline-none"
>
<Icon className="w-4 h-4" />
<span className="hidden sm:inline lg:inline">{tab.label}</span>
<span className="sm:hidden lg:hidden">{tab.label.split(' ')[0]}</span>
</TabsTrigger>
<Component />
</TabsContent>
);
})}
</TabsList>
{tabs.map((tab) => {
const Component = tab.component;
return (
<TabsContent
key={tab.id}
value={tab.id}
className="space-y-6 focus-visible:outline-none"
>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<tab.icon className="w-5 h-5" />
{tab.label}
</CardTitle>
</CardHeader>
<CardContent>
<Component />
</CardContent>
</Card>
</TabsContent>
);
})}
</Tabs>
</Tabs>
</div>
</div>
</div>