Skip to content

Commit e67ae09

Browse files
committed
feat: add weekly-table visual
1 parent ab79bc4 commit e67ae09

File tree

6 files changed

+413
-0
lines changed

6 files changed

+413
-0
lines changed

visuals/list.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,14 @@
1616
"preview": "visuals/cover-letter/preview.png",
1717
"schema": "visuals/cover-letter/schema.ts",
1818
"componentPath": "visuals/cover-letter/component.tsx"
19+
},
20+
{
21+
"name": "Weekly Table",
22+
"slug": "weekly-table",
23+
"author": "tscburak",
24+
"description": "A clear, flexible table to plan and track your week perfect for meal plans, workouts, study routines, chores and more.",
25+
"preview": "visuals/weekly-table/preview.png",
26+
"schema": "visuals/weekly-table/schema.ts",
27+
"componentPath": "visuals/weekly-table/component.tsx"
1928
}
2029
]

visuals/weekly-table/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# 📅 Weekly Table
2+
3+
---
4+
5+
## Overview
6+
7+
The **Weekly Table UI** is a simple, clean, and flexible layout for planning or tracking tasks, meals, routines, or any activities that repeat over a week.
8+
9+
It uses a **7-day grid**, where **one axis always represents the days of the week (Monday–Sunday)**, and the other axis can be customized to fit your needs — such as hours, time blocks (morning/afternoon/evening), tasks, or any custom category.
10+
11+
---
12+
13+
## Structure
14+
15+
* **X-axis or Y-axis**: Fixed to **7 days** (Monday–Sunday).
16+
* **Other axis**: Flexible. For example:
17+
18+
* **Time slots** (hours of the day)
19+
* **Parts of the day** (morning, afternoon, evening)
20+
* **Tasks or activities**
21+
* **People or rooms** (e.g., shifts or class schedules)
22+
23+
---
24+
25+
## Example Use Cases
26+
27+
* **Meal Planner**: Days on top, meals on the side (Breakfast, Lunch, Dinner).
28+
* **Workout Schedule**: Days on top, exercises or time blocks on the side.
29+
* **Class Timetable**: Days on top, class periods or time slots on the side.
30+
* **Daily Routine**: Days on the side, time blocks across the top.
31+
* **Shift Planner**: Days on one axis, employees or shifts on the other.
32+
33+
---
34+
35+
## Design Principles
36+
37+
**Simple & Clear**
38+
Easy to read and fill out — no clutter, just clean lines and clear sections.
39+
40+
**Print-Ready**
41+
Designed to be printable on standard paper.
42+
**No outer shadows** or heavy effects are used on the top-level container — shadows don’t translate well to printed pages and can waste ink.
43+
44+
**Customizable**
45+
You can adapt the non-day axis to any type of time block or category that suits your planning style.
46+
47+
---
48+
49+
## Printing Tips
50+
51+
* Use light or neutral colors for lines and cells.
52+
* Avoid heavy background fills.
53+
* Keep fonts legible and standard (e.g., Arial, Helvetica, or a clear serif).
54+
* Leave enough margin space for hole-punching or binder placement if needed.
55+
56+
---

visuals/weekly-table/component.tsx

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import React from 'react';
2+
import { WeeklyTableSchema, type WeeklyTableData } from './schema';
3+
import sampleData from './sample-data.json';
4+
5+
interface WeeklyTableProps {
6+
schema: typeof WeeklyTableSchema | null;
7+
data?: WeeklyTableData | null;
8+
}
9+
10+
// Extend Window interface for global function
11+
declare global {
12+
interface Window {
13+
__registerVisualComponent: (slug: string, component: React.ComponentType<WeeklyTableProps>) => void;
14+
}
15+
}
16+
17+
const WeeklyTable: React.FC<WeeklyTableProps> = ({ data }) => {
18+
// Use sample data if no data is provided
19+
const tableData = (data as WeeklyTableData) || sampleData;
20+
21+
const days = [
22+
{ key: 'monday', label: 'Mon', full: 'Monday' },
23+
{ key: 'tuesday', label: 'Tue', full: 'Tuesday' },
24+
{ key: 'wednesday', label: 'Wed', full: 'Wednesday' },
25+
{ key: 'thursday', label: 'Thu', full: 'Thursday' },
26+
{ key: 'friday', label: 'Fri', full: 'Friday' },
27+
{ key: 'saturday', label: 'Sat', full: 'Saturday' },
28+
{ key: 'sunday', label: 'Sun', full: 'Sunday' }
29+
];
30+
31+
const getThemeClasses = (theme?: string) => {
32+
switch (theme) {
33+
case 'gradient':
34+
return 'bg-gradient-to-br from-purple-50 via-blue-50 to-indigo-50';
35+
case 'minimal':
36+
return 'bg-gray-50';
37+
case 'colorful':
38+
return 'bg-gradient-to-br from-pink-50 via-yellow-50 to-green-50';
39+
default:
40+
return 'bg-white';
41+
}
42+
};
43+
44+
return (
45+
<div className={`min-h-full ${getThemeClasses(tableData.theme)} p-6 print:p-4`}>
46+
<div className="mx-auto max-w-7xl">
47+
{/* Header Section */}
48+
<div className="mb-8 text-center">
49+
<h1 className="mb-2 text-4xl font-bold text-gray-900 print:text-2xl">
50+
{tableData.title || 'Weekly Planner'}
51+
</h1>
52+
{tableData.subtitle && (
53+
<p className="text-lg text-gray-600 print:text-base">
54+
{tableData.subtitle}
55+
</p>
56+
)}
57+
</div>
58+
59+
{/* Weekly Table */}
60+
<div className="overflow-x-auto">
61+
<table className="w-full border-collapse overflow-hidden rounded-lg bg-white shadow-lg print:shadow-none">
62+
{/* Header Row */}
63+
<thead>
64+
<tr className="bg-gradient-to-r from-gray-100 to-gray-200">
65+
<th className="min-w-[200px] border border-gray-300 bg-gray-50 p-4 text-left font-bold text-gray-900 print:border-gray-600">
66+
<div className="text-sm uppercase tracking-wider text-gray-600">Activities</div>
67+
</th>
68+
{days.map((day) => (
69+
<th
70+
key={day.key}
71+
className="min-w-[140px] border border-gray-300 p-4 text-center font-bold text-gray-900 print:border-gray-600"
72+
>
73+
<div className="text-lg print:text-base">{day.label}</div>
74+
<div className="hidden text-xs font-normal text-gray-600 md:block print:hidden">
75+
{day.full}
76+
</div>
77+
</th>
78+
))}
79+
</tr>
80+
</thead>
81+
82+
{/* Body Rows */}
83+
<tbody>
84+
{tableData.rows.map((row, rowIndex) => (
85+
<tr
86+
key={row.id}
87+
className={rowIndex % 2 === 0 ? 'bg-white' : 'bg-gray-25 print:bg-white'}
88+
>
89+
{/* Row Header */}
90+
<td
91+
className="border border-gray-300 bg-gray-50 p-4 font-medium text-gray-900 print:border-gray-600 print:bg-gray-100"
92+
style={{
93+
borderLeftColor: row.color || '#e5e7eb',
94+
borderLeftWidth: '4px',
95+
borderLeftStyle: 'solid'
96+
}}
97+
>
98+
<div className="flex items-start space-x-3">
99+
{row.icon && (
100+
<span className="shrink-0 text-2xl print:text-xl">{row.icon}</span>
101+
)}
102+
<div className="min-w-0 flex-1">
103+
<h4 className="mb-1 text-sm font-semibold text-gray-900">
104+
{row.label}
105+
</h4>
106+
{row.time && (
107+
<p className="mb-1 text-xs text-gray-600">{row.time}</p>
108+
)}
109+
{row.category && (
110+
<span className=" inline-block rounded-full bg-gray-200 p-2 py-1 text-xs text-gray-700 print:bg-gray-300">
111+
{row.category}
112+
</span>
113+
)}
114+
</div>
115+
</div>
116+
</td>
117+
118+
{/* Day Cells */}
119+
{days.map((day) => {
120+
const item = tableData.items?.[day.key as keyof typeof tableData.items]?.[row.id];
121+
const itemColor = row.color;
122+
123+
return (
124+
<td
125+
key={`${row.id}-${day.key}`}
126+
className="border border-gray-300 bg-white p-3 align-top print:border-gray-600"
127+
style={itemColor && item ? {
128+
borderTopColor: itemColor,
129+
borderTopWidth: '3px',
130+
borderTopStyle: 'solid'
131+
} : {}}
132+
>
133+
{item ? (
134+
<div className="min-h-[80px] space-y-2 print:min-h-[60px]">
135+
{/* Content */}
136+
<p className="text-sm font-medium leading-tight text-gray-900 print:text-xs">
137+
{item.content}
138+
</p>
139+
140+
{/* Notes */}
141+
{item.notes && (
142+
<p className="border-t border-gray-200 pt-2 text-xs italic text-gray-600 print:border-gray-400">
143+
{item.notes}
144+
</p>
145+
)}
146+
</div>
147+
) : (
148+
<div className="flex h-20 items-center justify-center print:h-16">
149+
<span className="text-xs text-gray-400"></span>
150+
</div>
151+
)}
152+
</td>
153+
);
154+
})}
155+
</tr>
156+
))}
157+
</tbody>
158+
</table>
159+
</div>
160+
</div>
161+
</div>
162+
);
163+
};
164+
165+
// Export for dynamic loading
166+
export default WeeklyTable;
167+
168+
// Register component for dynamic loading
169+
if (typeof window !== 'undefined' && window.__registerVisualComponent) {
170+
window.__registerVisualComponent('weekly-table', WeeklyTable);
171+
}

visuals/weekly-table/metadata.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "Weekly Table",
3+
"version": "1.0.0",
4+
"description": "A clear, flexible table to plan and track your week perfect for meal plans, workouts, study routines, chores and more.",
5+
"author": "tscburak",
6+
"slug": "weekly-table"
7+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
{
2+
"title": "Weekly Fitness & Wellness Plan",
3+
"subtitle": "Transform your week with intentional habits",
4+
"theme": "gradient",
5+
"rows": [
6+
{
7+
"id": "morning-workout",
8+
"label": "Morning Workout",
9+
"icon": "💪",
10+
"color": "#ff6b6b",
11+
"time": "6:30 AM - 7:30 AM",
12+
"category": "Fitness"
13+
},
14+
{
15+
"id": "healthy-meals",
16+
"label": "Healthy Meals",
17+
"icon": "🥗",
18+
"color": "#4ecdc4",
19+
"time": "Throughout day",
20+
"category": "Nutrition"
21+
},
22+
{
23+
"id": "meditation",
24+
"label": "Meditation",
25+
"icon": "🧘‍♀️",
26+
"color": "#45b7d1",
27+
"time": "8:00 PM - 8:20 PM",
28+
"category": "Mindfulness"
29+
},
30+
{
31+
"id": "learning",
32+
"label": "Learning Time",
33+
"icon": "📚",
34+
"color": "#f9ca24",
35+
"time": "7:00 PM - 8:00 PM",
36+
"category": "Growth"
37+
}
38+
],
39+
"items": {
40+
"monday": {
41+
"morning-workout": {
42+
"content": "Full body strength training",
43+
"notes": "Focus on form and breathing"
44+
},
45+
"healthy-meals": {
46+
"content": "Green smoothie, quinoa bowl, grilled salmon"
47+
},
48+
"meditation": {
49+
"content": "10 min breathing exercise"
50+
},
51+
"learning": {
52+
"content": "React hooks deep dive"
53+
}
54+
},
55+
"tuesday": {
56+
"morning-workout": {
57+
"content": "Cardio + yoga flow"
58+
},
59+
"healthy-meals": {
60+
"content": "Oatmeal, salad wrap, stir-fry"
61+
},
62+
"meditation": {
63+
"content": "Guided mindfulness"
64+
},
65+
"learning": {
66+
"content": "TypeScript patterns"
67+
}
68+
},
69+
"wednesday": {
70+
"morning-workout": {
71+
"content": "Upper body focus",
72+
"notes": "Don't forget to stretch afterwards"
73+
},
74+
"healthy-meals": {
75+
"content": "Protein smoothie, Buddha bowl"
76+
},
77+
"meditation": {
78+
"content": "Walking meditation"
79+
},
80+
"learning": {
81+
"content": "Design patterns study"
82+
}
83+
},
84+
"thursday": {
85+
"morning-workout": {
86+
"content": "HIIT workout"
87+
},
88+
"healthy-meals": {
89+
"content": "Avocado toast, poke bowl"
90+
},
91+
"meditation": {
92+
"content": "Body scan meditation"
93+
},
94+
"learning": {
95+
"content": "Algorithm practice"
96+
}
97+
},
98+
"friday": {
99+
"morning-workout": {
100+
"content": "Active recovery - light yoga"
101+
},
102+
"healthy-meals": {
103+
"content": "Treat day with balance"
104+
},
105+
"meditation": {
106+
"content": "Gratitude practice"
107+
},
108+
"learning": {
109+
"content": "Weekend project planning"
110+
}
111+
},
112+
"saturday": {
113+
"morning-workout": {
114+
"content": "Outdoor adventure"
115+
},
116+
"healthy-meals": {
117+
"content": "Farmers market fresh"
118+
},
119+
"meditation": {
120+
"content": "Nature meditation"
121+
},
122+
"learning": {
123+
"content": "Side project work"
124+
}
125+
},
126+
"sunday": {
127+
"morning-workout": {
128+
"content": "Gentle stretching"
129+
},
130+
"healthy-meals": {
131+
"content": "Meal prep for the week",
132+
"notes": "Prepare containers and portions"
133+
},
134+
"meditation": {
135+
"content": "Week reflection"
136+
},
137+
"learning": {
138+
"content": "Week review and planning"
139+
}
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)