Refactor forms and uploader components UI with Card layout
Updated color, font, and asset forms to use Card and CardContent for improved structure and consistency. Enhanced input styles and hover effects for better user experience. Adjusted the image uploader background for a more cohesive design.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
// src/components/themes/event-theme-assets-uploader.tsx
|
// src/components/event-themes/event-theme-assets-uploader.tsx
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { ImageUploader } from "@/components/ui/image-uploader";
|
import { ImageUploader } from "@/components/ui/image-uploader";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
|
||||||
interface AssetImage {
|
interface AssetImage {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -76,69 +77,73 @@ export function EventThemeAssetsUploader({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<Label className="text-base">Theme Assets</Label>
|
<Label className="text-base font-semibold text-gray-900">
|
||||||
|
Theme Assets
|
||||||
|
</Label>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="flex items-center gap-1.5 text-sm font-medium"
|
||||||
onClick={handleAddAsset}
|
onClick={handleAddAsset}
|
||||||
>
|
>
|
||||||
<PlusCircle className="h-4 w-4 mr-2" />
|
<PlusCircle className="h-4 w-4" />
|
||||||
Add Asset
|
Add Asset
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-3">
|
||||||
{assets.map((asset, index) => (
|
{assets.map((asset, index) => (
|
||||||
<div
|
<Card
|
||||||
key={index}
|
key={index}
|
||||||
className="grid grid-cols-1 md:grid-cols-[1fr,2fr,auto] gap-4 items-start p-4 border rounded-md bg-gray-50"
|
className="overflow-hidden hover:shadow-md transition-shadow"
|
||||||
>
|
>
|
||||||
<div>
|
<CardContent className="grid grid-cols-1 md:grid-cols-[1fr,2fr,auto] gap-4 items-start p-4 pt-4">
|
||||||
<Label htmlFor={`asset-key-${index}`} className="mb-2 block">
|
<div>
|
||||||
Asset Key
|
<Label
|
||||||
</Label>
|
htmlFor={`asset-key-${index}`}
|
||||||
<Input
|
className="mb-2 block text-sm font-medium text-gray-700"
|
||||||
id={`asset-key-${index}`}
|
>
|
||||||
value={asset.key}
|
Asset Key
|
||||||
onChange={(e) => handleKeyChange(index, e.target.value)}
|
</Label>
|
||||||
placeholder="E.g., 'animal1', 'balloon', etc."
|
<Input
|
||||||
|
id={`asset-key-${index}`}
|
||||||
|
value={asset.key}
|
||||||
|
onChange={(e) => handleKeyChange(index, e.target.value)}
|
||||||
|
placeholder="E.g., 'animal1', 'balloon', etc."
|
||||||
|
className="focus:ring-2 focus:ring-primary/20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ImageUploader
|
||||||
|
id={`asset-url-${index}`}
|
||||||
|
label="Asset Image"
|
||||||
|
imageUrl={asset.url}
|
||||||
|
purpose="theme-asset"
|
||||||
|
onChange={(url) => handleUrlChange(index, url)}
|
||||||
|
aspectRatio="square"
|
||||||
|
maxWidth={400}
|
||||||
|
maxHeight={400}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<ImageUploader
|
<div className="flex items-end h-full pb-2">
|
||||||
id={`asset-url-${index}`}
|
<Button
|
||||||
label="Asset Image"
|
type="button"
|
||||||
imageUrl={asset.url}
|
variant="destructive"
|
||||||
purpose="theme-asset"
|
size="icon"
|
||||||
onChange={(url) => handleUrlChange(index, url)}
|
onClick={() => handleRemoveAsset(index)}
|
||||||
aspectRatio="square"
|
disabled={assets.length === 1}
|
||||||
maxWidth={400}
|
title="Remove Asset"
|
||||||
maxHeight={400}
|
className="hover:bg-red-600/90 transition-colors"
|
||||||
/>
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
<div className="flex items-end h-full pb-2">
|
</Button>
|
||||||
<Button
|
</div>
|
||||||
type="button"
|
</CardContent>
|
||||||
variant="destructive"
|
</Card>
|
||||||
size="icon"
|
|
||||||
onClick={() => handleRemoveAsset(index)}
|
|
||||||
disabled={assets.length === 1}
|
|
||||||
title="Remove Asset"
|
|
||||||
>
|
|
||||||
<Trash2 className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{assets.length === 0 && (
|
|
||||||
<p className="text-sm text-gray-500 italic">
|
|
||||||
No assets added. Click "Add Asset" to add theme assets.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,56 +327,64 @@ export function EventThemeForm({ theme, mode }: ThemeFormProps) {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
{colorInputs.map((input, index) => (
|
{colorInputs.map((input, index) => (
|
||||||
<div
|
<Card key={index} className="p-0 border overflow-hidden">
|
||||||
key={index}
|
<CardContent className="flex items-center space-x-4 p-3">
|
||||||
className="flex items-center space-x-4 p-3 border rounded-md bg-gray-50"
|
<div className="flex-1">
|
||||||
>
|
<Label
|
||||||
<div className="flex-1">
|
htmlFor={`color-name-${index}`}
|
||||||
<Label htmlFor={`color-name-${index}`} className="sr-only">
|
className="sr-only"
|
||||||
Color Name
|
>
|
||||||
</Label>
|
Color Name
|
||||||
<Input
|
</Label>
|
||||||
id={`color-name-${index}`}
|
<Input
|
||||||
value={input.name}
|
id={`color-name-${index}`}
|
||||||
onChange={(e) =>
|
value={input.name}
|
||||||
updateColorInput(index, "name", e.target.value)
|
onChange={(e) =>
|
||||||
}
|
updateColorInput(index, "name", e.target.value)
|
||||||
placeholder="Color name (e.g., primary, secondary)"
|
}
|
||||||
/>
|
placeholder="Color name (e.g., primary, secondary)"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 flex items-center space-x-2">
|
<div className="flex-1 flex items-center space-x-2">
|
||||||
<Input
|
<div className="relative w-16 h-10 overflow-hidden rounded-md border border-input">
|
||||||
type="color"
|
<Input
|
||||||
id={`color-value-${index}`}
|
type="color"
|
||||||
value={input.value}
|
id={`color-value-${index}`}
|
||||||
onChange={(e) =>
|
value={input.value}
|
||||||
updateColorInput(index, "value", e.target.value)
|
onChange={(e) =>
|
||||||
}
|
updateColorInput(index, "value", e.target.value)
|
||||||
className="w-16 h-10 p-1"
|
}
|
||||||
/>
|
className="absolute inset-0 opacity-0 w-full h-full cursor-pointer"
|
||||||
<Input
|
/>
|
||||||
type="text"
|
<div
|
||||||
value={input.value}
|
className="w-full h-full"
|
||||||
onChange={(e) =>
|
style={{ backgroundColor: input.value }}
|
||||||
updateColorInput(index, "value", e.target.value)
|
/>
|
||||||
}
|
</div>
|
||||||
placeholder="#RRGGBB"
|
<Input
|
||||||
className="flex-1"
|
type="text"
|
||||||
/>
|
value={input.value}
|
||||||
</div>
|
onChange={(e) =>
|
||||||
|
updateColorInput(index, "value", e.target.value)
|
||||||
|
}
|
||||||
|
placeholder="#RRGGBB"
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => removeColorInput(index)}
|
onClick={() => removeColorInput(index)}
|
||||||
disabled={colorInputs.length <= 1}
|
disabled={colorInputs.length <= 1}
|
||||||
title="Remove Color"
|
title="Remove Color"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -398,49 +406,51 @@ export function EventThemeForm({ theme, mode }: ThemeFormProps) {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
{fontInputs.map((input, index) => (
|
{fontInputs.map((input, index) => (
|
||||||
<div
|
<Card key={index} className="p-0 border overflow-hidden">
|
||||||
key={index}
|
<CardContent className="flex items-center space-x-4 p-3">
|
||||||
className="flex items-center space-x-4 p-3 border rounded-md bg-gray-50"
|
<div className="flex-1">
|
||||||
>
|
<Label htmlFor={`font-name-${index}`} className="sr-only">
|
||||||
<div className="flex-1">
|
Font Purpose
|
||||||
<Label htmlFor={`font-name-${index}`} className="sr-only">
|
</Label>
|
||||||
Font Purpose
|
<Input
|
||||||
</Label>
|
id={`font-name-${index}`}
|
||||||
<Input
|
value={input.name}
|
||||||
id={`font-name-${index}`}
|
onChange={(e) =>
|
||||||
value={input.name}
|
updateFontInput(index, "name", e.target.value)
|
||||||
onChange={(e) =>
|
}
|
||||||
updateFontInput(index, "name", e.target.value)
|
placeholder="Font purpose (e.g., heading, body)"
|
||||||
}
|
/>
|
||||||
placeholder="Font purpose (e.g., heading, body)"
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<Label htmlFor={`font-value-${index}`} className="sr-only">
|
<Label
|
||||||
Font Name
|
htmlFor={`font-value-${index}`}
|
||||||
</Label>
|
className="sr-only"
|
||||||
<Input
|
>
|
||||||
id={`font-value-${index}`}
|
Font Name
|
||||||
value={input.value}
|
</Label>
|
||||||
onChange={(e) =>
|
<Input
|
||||||
updateFontInput(index, "value", e.target.value)
|
id={`font-value-${index}`}
|
||||||
}
|
value={input.value}
|
||||||
placeholder="Font name (e.g., Inter, Roboto)"
|
onChange={(e) =>
|
||||||
/>
|
updateFontInput(index, "value", e.target.value)
|
||||||
</div>
|
}
|
||||||
|
placeholder="Font name (e.g., Inter, Roboto)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => removeFontInput(index)}
|
onClick={() => removeFontInput(index)}
|
||||||
disabled={fontInputs.length <= 1}
|
disabled={fontInputs.length <= 1}
|
||||||
title="Remove Font"
|
title="Remove Font"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ export function ImageUploader({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`relative border-2 border-dashed border-gray-300 rounded-md ${getAspectRatioClass()} overflow-hidden bg-gray-50 hover:bg-gray-100 transition-colors`}
|
className={`relative border-2 border-dashed border-border rounded-md ${getAspectRatioClass()}
|
||||||
|
overflow-hidden bg-muted/30 hover:bg-muted/50 transition-colors`}
|
||||||
>
|
>
|
||||||
{/* Hidden file input */}
|
{/* Hidden file input */}
|
||||||
<input
|
<input
|
||||||
|
|||||||
Reference in New Issue
Block a user