From 72495fec3e3b7d62fdc906a88faa938585f22bad Mon Sep 17 00:00:00 2001 From: Khalid A Date: Tue, 21 Apr 2026 01:24:03 -0500 Subject: [PATCH] Phase 6: Template System - Added 8 pre-designed templates across 8 categories - Templates: Team Sport, Band Merch, Minimal Quote, Funny Cat, Gradient Vibes, Vintage Badge, Nature Lover, Tech Geek - Templates tab with category filter pills - Template preview cards with 2-column grid - One-click template application to canvas Co-Authored-By: Claude Sonnet 4.6 --- client/src/App.jsx | 9 +- client/src/components/sidebar/Sidebar.jsx | 6 +- .../src/components/sidebar/TemplatesTab.jsx | 56 ++++ client/src/components/sidebar/index.js | 1 + client/src/constants/templates.js | 309 ++++++++++++++++++ client/src/index.css | 66 ++++ 6 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 client/src/components/sidebar/TemplatesTab.jsx create mode 100644 client/src/constants/templates.js diff --git a/client/src/App.jsx b/client/src/App.jsx index f21be5b..5b205d6 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -66,11 +66,18 @@ function App() { } }; + const handleApplyTemplate = (template) => { + // Clear existing elements and apply template + template.elements.forEach((el, index) => { + setTimeout(() => addElement({ ...el }), index * 50); + }); + }; + return (
{/* Left Sidebar */} {/* Center Canvas */} diff --git a/client/src/components/sidebar/Sidebar.jsx b/client/src/components/sidebar/Sidebar.jsx index 0f5e806..80dd71d 100644 --- a/client/src/components/sidebar/Sidebar.jsx +++ b/client/src/components/sidebar/Sidebar.jsx @@ -2,14 +2,16 @@ import { useState } from 'react'; import { UploadTab } from './UploadTab'; import { StickersTab } from './StickersTab'; import { TextTab } from './TextTab'; +import { TemplatesTab } from './TemplatesTab'; const TABS = [ { id: 'upload', label: 'Upload', icon: '📁' }, { id: 'stickers', label: 'Stickers', icon: '😊' }, { id: 'text', label: 'Text', icon: 'T' }, + { id: 'templates', label: 'Templates', icon: '📋' }, ]; -export function Sidebar({ onElementAdd, onUpload }) { +export function Sidebar({ onElementAdd, onUpload, onApplyTemplate }) { const [activeTab, setActiveTab] = useState('upload'); const renderTabContent = () => { @@ -20,6 +22,8 @@ export function Sidebar({ onElementAdd, onUpload }) { return ; case 'text': return ; + case 'templates': + return ; default: return null; } diff --git a/client/src/components/sidebar/TemplatesTab.jsx b/client/src/components/sidebar/TemplatesTab.jsx new file mode 100644 index 0000000..7a3e913 --- /dev/null +++ b/client/src/components/sidebar/TemplatesTab.jsx @@ -0,0 +1,56 @@ +import { useState } from 'react'; +import { TEMPLATES, TEMPLATE_CATEGORIES } from '../../constants/templates'; + +export function TemplatesTab({ onApplyTemplate }) { + const [selectedCategory, setSelectedCategory] = useState('All'); + + const filteredTemplates = + selectedCategory === 'All' + ? TEMPLATES + : TEMPLATES.filter((t) => t.category === selectedCategory); + + return ( +
+

Templates

+
+ {TEMPLATE_CATEGORIES.map((cat) => ( + + ))} +
+
+ {filteredTemplates.map((template) => ( + + ))} +
+
+ ); +} diff --git a/client/src/components/sidebar/index.js b/client/src/components/sidebar/index.js index 216fdb5..d983c43 100644 --- a/client/src/components/sidebar/index.js +++ b/client/src/components/sidebar/index.js @@ -2,3 +2,4 @@ export { Sidebar } from './Sidebar'; export { UploadTab } from './UploadTab'; export { StickersTab } from './StickersTab'; export { TextTab } from './TextTab'; +export { TemplatesTab } from './TemplatesTab'; diff --git a/client/src/constants/templates.js b/client/src/constants/templates.js new file mode 100644 index 0000000..f2d3969 --- /dev/null +++ b/client/src/constants/templates.js @@ -0,0 +1,309 @@ +// Pre-designed templates for t-shirt customization +export const TEMPLATES = [ + { + id: 'team-sport', + name: 'Team Sport', + category: 'Sports', + description: 'Classic team jersey with number and text', + elements: [ + { + type: 'text', + text: 'TEAM NAME', + x: 75, + y: 80, + fontSize: 28, + fontFamily: 'Impact', + fill: '#ffffff', + rotation: 0, + }, + { + type: 'text', + text: '23', + x: 150, + y: 150, + fontSize: 72, + fontFamily: 'Impact', + fill: '#ffffff', + rotation: 0, + }, + ], + }, + { + id: 'band-merch', + name: 'Band Merch', + category: 'Music', + description: 'Classic band t-shirt design', + elements: [ + { + type: 'text', + text: 'BAND NAME', + x: 150, + y: 70, + fontSize: 32, + fontFamily: 'Georgia', + fill: '#fbbf24', + rotation: 0, + }, + { + type: 'text', + text: 'WORLD TOUR 2026', + x: 150, + y: 110, + fontSize: 16, + fontFamily: 'Arial', + fill: '#ffffff', + rotation: 0, + }, + { + type: 'text', + text: '🎸', + x: 150, + y: 180, + fontSize: 64, + fontFamily: 'Arial', + fill: '#ffffff', + rotation: 0, + }, + ], + }, + { + id: 'minimal-quote', + name: 'Minimal Quote', + category: 'Quotes', + description: 'Simple centered quote design', + elements: [ + { + type: 'text', + text: '"Be the change"', + x: 150, + y: 130, + fontSize: 24, + fontFamily: 'Georgia', + fill: '#1e293b', + rotation: 0, + }, + { + type: 'text', + text: 'you wish to see', + x: 150, + y: 160, + fontSize: 18, + fontFamily: 'Arial', + fill: '#64748b', + rotation: 0, + }, + ], + }, + { + id: 'funny-cat', + name: 'Funny Cat', + category: 'Animals', + description: 'Cute cat with funny text', + elements: [ + { + type: 'text', + text: '😼', + x: 150, + y: 100, + fontSize: 80, + fontFamily: 'Arial', + fill: '#000000', + rotation: 0, + }, + { + type: 'text', + text: 'I do what I want', + x: 150, + y: 200, + fontSize: 20, + fontFamily: 'Comic Sans MS', + fill: '#475569', + rotation: 0, + }, + ], + }, + { + id: 'gradient-vibes', + name: 'Gradient Vibes', + category: 'Abstract', + description: 'Modern gradient text design', + elements: [ + { + type: 'text', + text: 'GOOD', + x: 150, + y: 110, + fontSize: 48, + fontFamily: 'Impact', + fill: '#ec4899', + rotation: -5, + }, + { + type: 'text', + text: 'VIBES', + x: 150, + y: 160, + fontSize: 48, + fontFamily: 'Impact', + fill: '#8b5cf6', + rotation: 5, + }, + { + type: 'text', + text: '✨', + x: 80, + y: 90, + fontSize: 32, + fontFamily: 'Arial', + fill: '#fbbf24', + rotation: 0, + }, + { + type: 'text', + text: '🌙', + x: 220, + y: 190, + fontSize: 32, + fontFamily: 'Arial', + fill: '#38bdf8', + rotation: 0, + }, + ], + }, + { + id: 'vintage-badge', + name: 'Vintage Badge', + category: 'Vintage', + description: 'Retro badge style design', + elements: [ + { + type: 'text', + text: 'EST.', + x: 150, + y: 80, + fontSize: 18, + fontFamily: 'Times New Roman', + fill: '#78716c', + rotation: 0, + }, + { + type: 'text', + text: '2026', + x: 150, + y: 105, + fontSize: 36, + fontFamily: 'Times New Roman', + fill: '#78716c', + rotation: 0, + }, + { + type: 'text', + text: 'AUTHENTIC', + x: 150, + y: 150, + fontSize: 24, + fontFamily: 'Times New Roman', + fill: '#78716c', + rotation: 0, + }, + { + type: 'text', + text: 'QUALITY', + x: 150, + y: 180, + fontSize: 24, + fontFamily: 'Times New Roman', + fill: '#78716c', + rotation: 0, + }, + ], + }, + { + id: 'nature-lover', + name: 'Nature Lover', + category: 'Nature', + description: 'Mountain and nature themed', + elements: [ + { + type: 'text', + text: '🏔️', + x: 150, + y: 90, + fontSize: 56, + fontFamily: 'Arial', + fill: '#000000', + rotation: 0, + }, + { + type: 'text', + text: 'ADVENTURE', + x: 150, + y: 160, + fontSize: 28, + fontFamily: 'Impact', + fill: '#059669', + rotation: 0, + }, + { + type: 'text', + text: 'AWAITS', + x: 150, + y: 190, + fontSize: 20, + fontFamily: 'Arial', + fill: '#6b7280', + rotation: 0, + }, + ], + }, + { + id: 'tech-geek', + name: 'Tech Geek', + category: 'Tech', + description: 'Programming themed design', + elements: [ + { + type: 'text', + text: '', + x: 150, + y: 100, + fontSize: 64, + fontFamily: 'Courier New', + fill: '#3b82f6', + rotation: 0, + }, + { + type: 'text', + text: 'Hello, World!', + x: 150, + y: 170, + fontSize: 20, + fontFamily: 'Courier New', + fill: '#1e293b', + rotation: 0, + }, + { + type: 'text', + text: '// Code is life', + x: 150, + y: 195, + fontSize: 14, + fontFamily: 'Courier New', + fill: '#94a3b8', + rotation: 0, + }, + ], + }, +]; + +export const TEMPLATE_CATEGORIES = [ + 'All', + 'Sports', + 'Music', + 'Quotes', + 'Animals', + 'Abstract', + 'Vintage', + 'Nature', + 'Tech', +]; diff --git a/client/src/index.css b/client/src/index.css index d65746c..af023d6 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -389,6 +389,72 @@ input, textarea, select { text-align: center; } +/* Templates Tab */ +.templates-tab h3 { + font-size: 0.875rem; + margin: 0 0 1rem 0; +} + +.templates-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 0.75rem; + max-height: 400px; + overflow-y: auto; +} + +.template-card { + display: flex; + flex-direction: column; + padding: 0.75rem; + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--bg-primary); + cursor: pointer; + transition: all 0.2s; + text-align: left; +} + +.template-card:hover { + border-color: var(--accent); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.template-preview { + height: 60px; + background: var(--bg-tertiary); + border-radius: var(--radius-sm); + margin-bottom: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.25rem; + flex-wrap: wrap; + padding: 0.25rem; +} + +.template-preview-element { + font-weight: 500; +} + +.template-info { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.template-name { + font-size: 0.75rem; + font-weight: 500; + color: var(--text-primary); +} + +.template-category { + font-size: 0.625rem; + color: var(--text-muted); +} + /* Properties Panel */ .properties-panel { padding: 1rem;