TO DO LIST APP SOURCE CODE

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JOBSDONE APP v2 - Supabase Edition</title>
    
    <!-- External Dependencies (Blogger Compatible) -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Outfit:wght@400;600;800&family=Space+Grotesk:wght@400;600;700&display=swap" rel="stylesheet">
    <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@supabase/supabase-js@2"></script>
    <script src="https://unpkg.com/lucide@latest"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <style type="text/tailwindcss">
        @layer base {
            html { 
                font-family: 'Inter', sans-serif; 
                -webkit-font-smoothing: antialiased; 
                background: #F8FAFC;
            }
            h1, h2, h3 { font-family: 'Outfit', sans-serif; }
            .font-mono { font-family: 'JetBrains Mono', monospace; }
        }
        @layer components {
            .btn-primary {
                @apply w-full h-14 bg-indigo-600 hover:bg-indigo-700 text-white font-bold rounded-2xl shadow-lg shadow-indigo-100 transition-all active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2;
            }
            .input-style {
                @apply w-full px-4 py-3 bg-white border border-slate-200 rounded-xl focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500 outline-none transition-all placeholder:text-slate-400;
            }
            .card-gradient {
                @apply bg-white border border-slate-100 shadow-sm hover:shadow-md transition-shadow duration-300;
            }
            .badge {
                @apply inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-[10px] font-bold uppercase tracking-wider;
            }
        }
        /* Custom scroller for Blogger integration */
        ::-webkit-scrollbar { width: 6px; }
        ::-webkit-scrollbar-track { background: transparent; }
        ::-webkit-scrollbar-thumb { background: #E2E8F0; border-radius: 10px; }
        ::-webkit-scrollbar-thumb:hover { background: #CBD5E1; }
    </style>
</head>
<body class="selection:bg-indigo-100 selection:text-indigo-900">
    <div id="root"></div>

    <script type="text/babel">
        /**
         * JOBSDONE APP v2 (Supabase Edition)
         * Optimized for Blogger / Blogspot HTML Gadgets
         */
        const { useState, useEffect, useMemo, useCallback } = React;
        
        // --- Configuration (Injected directly for Blogger compatibility) ---
        const SUPABASE_URL = 'https://ipjlqlsaxhvzvelcgqav.supabase.co';
        const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlwamxxbHNheGh2enZlbGNncWF2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzY2MDA4NTUsImV4cCI6MjA5MjE3Njg1NX0.ceuuTpDjAHS4vmwgczr6pV0Kf1DQo97p25nPfW-JLTQ';
        const TABLE_NAME = 'tasks';
        
        const supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_KEY);

        const CATEGORIES = [
            "TO DO LIST (FILING)",
            "TO DO LIST (PERTUKANGAN)",
            "TO DO LIST (more priority)",
            "TO DO LIST (less priority)",
            "TO be BOUGHT",
            "TO DO didepan LAPTOP"
        ];

        const STATUSES = ["ongoing", "finished"];

        // Helper for Lucide icons in UMD environment
        const Icon = ({ name, className }) => {
            useEffect(() => {
                lucide.createIcons();
            }, [name]);
            return <i data-lucide={name} className={className}></i>;
        };

        // --- UI Components ---
        const Modal = ({ isOpen, onClose, title, children }) => {
            if (!isOpen) return null;
            return (
                <div className="fixed inset-0 z-[9999] flex items-center justify-center p-4">
                    <div className="absolute inset-0 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-300" onClick={onClose}></div>
                    <div className="relative w-full max-w-lg overflow-hidden bg-white rounded-3xl shadow-2xl animate-in zoom-in-95 fade-in duration-300">
                        <div className="flex items-center justify-between px-6 py-5 border-b border-slate-50">
                            <h3 className="text-xl font-extrabold text-slate-800">{title}</h3>
                            <button onClick={onClose} className="p-2 hover:bg-slate-50 rounded-full transition-colors text-slate-400 hover:text-slate-600">
                                <Icon name="x" className="w-5 h-5" />
                            </button>
                        </div>
                        <div className="p-6 md:p-8">
                            {children}
                        </div>
                    </div>
                </div>
            );
        };

        function App() {
            const [tasks, setTasks] = useState([]);
            const [loading, setLoading] = useState(true);
            const [search, setSearch] = useState('');
            const [sortOrder, setSortOrder] = useState('desc');
            const [isSaving, setIsSaving] = useState(false);
            const [error, setError] = useState(null);

            const [formData, setFormData] = useState({
                category: CATEGORIES[0],
                overall_status: STATUSES[0],
                task_title: ''
            });

            const [editingTask, setEditingTask] = useState(null);
            const [viewingTask, setViewingTask] = useState(null);

            const fetchTasks = async () => {
                setLoading(true);
                try {
                    const { data, error } = await supabaseClient
                        .from(TABLE_NAME)
                        .select('*');
                    
                    if (error) throw error;
                    setTasks(data || []);
                    setError(null);
                } catch (err) {
                    setError('Database connection error. Please verify configuration.');
                } finally {
                    setLoading(false);
                }
            };

            useEffect(() => {
                fetchTasks();
            }, []);

            const handleSave = async (e) => {
                if (e) e.preventDefault();
                const currentData = editingTask || formData;
                if (!currentData.task_title.trim()) return alert("Please enter a task title.");

                setIsSaving(true);
                const payload = {
                    category: currentData.category,
                    overall_status: currentData.overall_status,
                    task_title: currentData.task_title
                };

                try {
                    let res;
                    if (editingTask) {
                        res = await supabaseClient
                            .from(TABLE_NAME)
                            .update(payload)
                            .eq('_key', editingTask._key);
                    } else {
                        res = await supabaseClient
                            .from(TABLE_NAME)
                            .insert([{ 
                                ...payload, 
                                _key: Date.now() + "_" + Math.floor(Math.random() * 1000) 
                            }]);
                    }

                    if (res.error) throw res.error;
                    
                    setFormData({ ...formData, task_title: '' });
                    setEditingTask(null);
                    fetchTasks();
                } catch (err) {
                    alert("Sync Error: " + err.message);
                } finally {
                    setIsSaving(false);
                }
            };

            const handleDelete = async (key) => {
                if (!confirm('Permanently delete this task?')) return;
                try {
                    const { error } = await supabaseClient
                        .from(TABLE_NAME)
                        .delete()
                        .eq('_key', key);
                    if (error) throw error;
                    fetchTasks();
                } catch (err) { alert(err.message); }
            };

            const filteredTasks = useMemo(() => {
                let result = tasks.filter(t => {
                    const cat = (t.category || '').toLowerCase();
                    const status = (t.overall_status || '').toLowerCase();
                    const title = (t.task_title || '').toLowerCase();
                    const s = search.toLowerCase();
                    return cat.includes(s) || status.includes(s) || title.includes(s);
                });

                result.sort((a, b) => {
                    const valA = a.created_at || a._key;
                    const valB = b.created_at || b._key;
                    return sortOrder === 'desc' ? (valB > valA ? 1 : -1) : (valA > valB ? 1 : -1);
                });

                return result;
            }, [tasks, search, sortOrder]);

            return (
                <div className="min-h-screen py-8 px-4 md:py-16">
                    <div className="max-w-4xl mx-auto">
                        {/* Header Section */}
                        <header className="mb-12 text-center">
                            <div className="inline-flex items-center justify-center w-16 h-16 mb-6 bg-indigo-600 text-white rounded-3xl shadow-xl shadow-indigo-200 rotate-3 transform hover:rotate-0 transition-transform">
                                <Icon name="check-check" className="w-8 h-8" />
                            </div>
                            <h1 className="text-5xl font-black tracking-tight text-slate-900 mb-2">
                                JOBSDONE <span className="text-indigo-600 font-extrabold italic opacity-90">APP</span>
                            </h1>
                            <div className="flex items-center justify-center gap-4 text-slate-400 font-bold text-[10px] uppercase tracking-[0.3em]">
                                <span className="w-8 h-[2px] bg-indigo-100"></span>
                                Household Productivity Systems
                                <span className="w-8 h-[2px] bg-indigo-100"></span>
                            </div>
                        </header>

                        {/* Connection Error */}
                        {error && (
                            <div className="mb-8 p-4 bg-rose-50 border border-rose-100 rounded-2xl text-rose-600 flex items-center gap-4 animate-bounce">
                                <Icon name="alert-circle" className="w-6 h-6 shrink-0" />
                                <span className="text-sm font-bold tracking-tight">{error}</span>
                            </div>
                        )}

                        {/* Dashboard Logic */}
                        <div className="grid grid-cols-1 gap-8">
                            {/* Entry Form */}
                            <section className="bg-white p-8 rounded-[2.5rem] shadow-sm border border-slate-100 card-gradient">
                                <form onSubmit={handleSave} className="space-y-8">
                                    <div className="flex items-center gap-2 text-xs font-black text-slate-400 uppercase tracking-widest">
                                        <Icon name="plus-circle" className="w-4 h-4 text-indigo-400" />
                                        Task Deployment
                                    </div>
                                    <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                                        <div className="space-y-2">
                                            <label className="text-[11px] font-black text-slate-500 uppercase tracking-wider ml-1">Select Category</label>
                                            <select 
                                                className="input-style font-semibold text-slate-700"
                                                value={formData.category}
                                                onChange={e => setFormData({...formData, category: e.target.value})}
                                            >
                                                {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
                                            </select>
                                        </div>
                                        <div className="space-y-2">
                                            <label className="text-[11px] font-black text-slate-500 uppercase tracking-wider ml-1">Current Status</label>
                                            <select 
                                                className="input-style font-semibold text-slate-700"
                                                value={formData.overall_status}
                                                onChange={e => setFormData({...formData, overall_status: e.target.value})}
                                            >
                                                {STATUSES.map(s => <option key={s} value={s}>{s}</option>)}
                                            </select>
                                        </div>
                                    </div>
                                    <div className="space-y-2">
                                        <label className="text-[11px] font-black text-slate-500 uppercase tracking-wider ml-1">Task Specification</label>
                                        <textarea 
                                            placeholder="Specify the requirements for this mission..."
                                            className="input-style min-h-[140px] resize-none font-medium leading-relaxed italic"
                                            value={formData.task_title}
                                            onChange={e => setFormData({...formData, task_title: e.target.value})}
                                        />
                                    </div>
                                    <button 
                                        type="submit" 
                                        disabled={isSaving || !!error}
                                        className="btn-primary group"
                                    >
                                        {isSaving ? (
                                            <Icon name="refresh-cw" className="w-5 h-5 animate-spin" />
                                        ) : (
                                            <>
                                                <Icon name="zap" className="w-5 h-5 group-hover:scale-125 transition-transform" />
                                                <span>DEPLOY TASK</span>
                                            </>
                                        )}
                                    </button>
                                </form>
                            </section>

                            {/* Viewport Controls */}
                            <section className="flex flex-col md:flex-row gap-4 items-center justify-between">
                                <div className="relative w-full md:max-w-xs group">
                                    <Icon name="search" className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-300 group-focus-within:text-indigo-400 transition-colors" />
                                    <input 
                                        type="text" 
                                        placeholder="Scan intelligence..." 
                                        className="w-full pl-12 pr-4 py-4 bg-white border border-slate-200 rounded-3xl shadow-sm focus:ring-4 focus:ring-indigo-500/10 outline-none font-medium placeholder:italic transition-all"
                                        value={search}
                                        onChange={e => setSearch(e.target.value)}
                                    />
                                </div>
                                <div className="flex items-center gap-4 bg-white p-2 border border-slate-100 rounded-3xl shadow-sm shrink-0">
                                    <span className="text-[10px] font-black text-slate-400 uppercase tracking-widest pl-4">Priority Sequence:</span>
                                    <select 
                                        className="px-6 py-2 bg-slate-50 border-none rounded-2xl outline-none text-xs font-bold text-slate-600 cursor-pointer hover:bg-slate-100 transition-colors"
                                        value={sortOrder}
                                        onChange={e => setSortOrder(e.target.value)}
                                    >
                                        <option value="desc">NEXUS (Newest)</option>
                                        <option value="asc">LEGACY (Oldest)</option>
                                    </select>
                                </div>
                            </section>

                            {/* Main Register */}
                            <section className="bg-white rounded-[2.5rem] shadow-sm border border-slate-100 overflow-hidden card-gradient">
                                <div className="overflow-x-auto scroller">
                                    <table className="w-full text-left">
                                        <thead>
                                            <tr className="bg-slate-50/80 border-b border-slate-50 uppercase tracking-[0.2em] text-[9px] font-black text-slate-400">
                                                <th className="px-8 py-5">Metadata</th>
                                                <th className="px-8 py-5">Objective Parameters</th>
                                                <th className="px-8 py-5 text-right">Operations</th>
                                            </tr>
                                        </thead>
                                        <tbody className="divide-y divide-slate-50">
                                            {loading ? (
                                                <tr>
                                                    <td colSpan="3" className="px-8 py-32 text-center">
                                                        <div className="flex flex-col items-center gap-6">
                                                            <div className="w-12 h-12 border-4 border-indigo-600 border-t-transparent rounded-full animate-spin"></div>
                                                            <div className="font-black text-indigo-400 text-xs uppercase tracking-[0.5em] animate-pulse">Establishing Secure Uplink...</div>
                                                        </div>
                                                    </td>
                                                </tr>
                                            ) : filteredTasks.length === 0 ? (
                                                <tr>
                                                    <td colSpan="3" className="px-8 py-32 text-center text-slate-300 font-bold italic tracking-tight">
                                                        No intelligence found matching search parameters.
                                                    </td>
                                                </tr>
                                            ) : (
                                                filteredTasks.map(task => (
                                                    <tr key={task._key} className="group hover:bg-indigo-50/30 transition-all">
                                                        <td className="px-8 py-8 w-64 align-top">
                                                            <div className="flex items-center gap-2 mb-3">
                                                                <Icon name="tag" className="w-3 h-3 text-indigo-400" />
                                                                <span className="text-[10px] font-black text-slate-500 uppercase tracking-tighter truncate max-w-[150px]">
                                                                    {task.category}
                                                                </span>
                                                            </div>
                                                            <div className={`badge ${
                                                                task.overall_status === 'finished' 
                                                                    ? 'bg-emerald-50 text-emerald-600' 
                                                                    : 'bg-amber-50 text-amber-600'
                                                            }`}>
                                                                <Icon name="activity" className="w-3 h-3" />
                                                                {task.overall_status}
                                                            </div>
                                                        </td>
                                                        <td className="px-8 py-8 align-top">
                                                            <div className="text-slate-700 font-bold text-sm leading-relaxed italic line-clamp-3 hover:line-clamp-none transition-all cursor-default">
                                                                {task.task_title}
                                                            </div>
                                                        </td>
                                                        <td className="px-8 py-8 text-right whitespace-nowrap align-top">
                                                            <div className="flex items-center justify-end gap-2">
                                                                <button 
                                                                    onClick={() => setViewingTask(task)}
                                                                    className="p-3 rounded-2xl text-slate-400 hover:text-indigo-600 hover:bg-indigo-100/50 transition-all hover:rotate-12"
                                                                    title="Analyze"
                                                                >
                                                                    <Icon name="eye" className="w-5 h-5" />
                                                                </button>
                                                                <button 
                                                                    onClick={() => setEditingTask(task)}
                                                                    className="p-3 rounded-2xl text-slate-400 hover:text-indigo-600 hover:bg-indigo-100/50 transition-all hover:-rotate-12"
                                                                    title="Modify"
                                                                >
                                                                    <Icon name="edit-3" className="w-5 h-5" />
                                                                </button>
                                                                <button 
                                                                    onClick={() => handleDelete(task._key)}
                                                                    className="p-3 rounded-2xl text-slate-400 hover:text-rose-600 hover:bg-rose-100/50 transition-all hover:scale-125"
                                                                    title="Terminate"
                                                                >
                                                                    <Icon name="trash-2" className="w-5 h-5" />
                                                                </button>
                                                            </div>
                                                        </td>
                                                    </tr>
                                                ))
                                            )}
                                        </tbody>
                                    </table>
                                </div>
                            </section>
                        </div>

                        {/* System Footer */}
                        <footer className="mt-20 py-10 border-t border-slate-100 text-center">
                            <div className="flex justify-center gap-2 mb-6">
                                <span className="w-2 h-2 rounded-full bg-indigo-600 animate-ping"></span>
                                <span className="w-2 h-2 rounded-full bg-indigo-400 animate-pulse"></span>
                                <span className="w-2 h-2 rounded-full bg-indigo-200"></span>
                            </div>
                            <div className="text-[10px] font-black text-slate-300 uppercase tracking-[0.6em] mb-2">
                                Enterprise Productivity Infrastructure
                            </div>
                            <div className="text-[8px] font-bold text-slate-200 uppercase tracking-[0.2em]">
                                JOBSDONE CORE v2.4.0 (SUPABASE-JS-RUNTIME)
                            </div>
                        </footer>
                    </div>

                    {/* Modal Overlays */}
                    <Modal 
                        isOpen={!!viewingTask} 
                        onClose={() => setViewingTask(null)}
                        title="Objective Analysis"
                    >
                        {viewingTask && (
                            <div className="space-y-6">
                                <div className="grid grid-cols-2 gap-4">
                                    <div className="p-5 bg-slate-50 rounded-[1.5rem]">
                                        <div className="text-[9px] font-black text-slate-400 uppercase tracking-widest mb-2">Sector</div>
                                        <div className="text-xs font-black text-slate-700 uppercase">{viewingTask.category}</div>
                                    </div>
                                    <div className={`p-5 rounded-[1.5rem] border ${
                                        viewingTask.overall_status === 'finished' ? 'bg-emerald-50 border-emerald-100 text-emerald-600' : 'bg-amber-50 border-amber-100 text-amber-600'
                                    }`}>
                                        <div className="text-[9px] font-black opacity-60 uppercase tracking-widest mb-2">Status</div>
                                        <div className="text-xs font-black uppercase tracking-widest">{viewingTask.overall_status}</div>
                                    </div>
                                </div>
                                <div className="p-8 bg-indigo-50/50 rounded-[2rem] border border-indigo-100">
                                    <div className="text-[9px] font-black text-indigo-400 uppercase tracking-widest mb-4">Objective Details</div>
                                    <div className="text-slate-800 font-bold italic leading-loose text-lg text-center">
                                        "{viewingTask.task_title}"
                                    </div>
                                </div>
                                <button 
                                    onClick={() => setViewingTask(null)}
                                    className="w-full h-14 bg-slate-900 text-white font-black rounded-2xl active:scale-95 transition-transform uppercase tracking-widest text-xs"
                                >
                                    Dismiss Analysis
                                </button>
                            </div>
                        )}
                    </Modal>

                    <Modal 
                        isOpen={!!editingTask} 
                        onClose={() => setEditingTask(null)}
                        title="Modify Specification"
                    >
                        {editingTask && (
                            <form onSubmit={handleSave} className="space-y-6">
                                <div className="grid grid-cols-2 gap-4">
                                    <div className="space-y-2">
                                        <label className="text-[10px] font-black text-slate-400 uppercase tracking-wider ml-1">Category</label>
                                        <select 
                                            className="input-style font-bold text-slate-700"
                                            value={editingTask.category}
                                            onChange={e => setEditingTask({...editingTask, category: e.target.value})}
                                        >
                                            {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
                                        </select>
                                    </div>
                                    <div className="space-y-2">
                                        <label className="text-[10px] font-black text-slate-400 uppercase tracking-wider ml-1">Status</label>
                                        <select 
                                            className="input-style font-bold text-slate-700"
                                            value={editingTask.overall_status}
                                            onChange={e => setEditingTask({...editingTask, overall_status: e.target.value})}
                                        >
                                            {STATUSES.map(s => <option key={s} value={s}>{s}</option>)}
                                        </select>
                                    </div>
                                </div>
                                <div className="space-y-2">
                                    <label className="text-[10px] font-black text-slate-400 uppercase tracking-wider ml-1">Task specification</label>
                                    <textarea 
                                        className="input-style min-h-[160px] resize-none font-bold italic"
                                        value={editingTask.task_title}
                                        onChange={e => setEditingTask({...editingTask, task_title: e.target.value})}
                                    />
                                </div>
                                <div className="flex gap-4 pt-4">
                                    <button 
                                        type="button"
                                        onClick={() => setEditingTask(null)}
                                        className="flex-1 h-14 bg-slate-50 text-slate-400 font-black rounded-2xl hover:bg-slate-100 transition-colors uppercase text-xs"
                                    >
                                        Abort
                                    </button>
                                    <button 
                                        type="submit"
                                        disabled={isSaving}
                                        className="flex-1 h-14 bg-indigo-600 text-white font-black rounded-2xl shadow-xl shadow-indigo-100 hover:bg-indigo-700 active:scale-95 transition-all text-xs"
                                    >
                                        {isSaving ? "SYNCHRONIZING..." : "COMMIT CHANGES"}
                                    </button>
                                </div>
                            </form>
                        )}
                    </Modal>
                </div>
            );
        }

        const root = ReactDOM.createRoot(document.getElementById('root'));
        root.render(<App />);
    </script>
</body>
</html>

Comments