Skip to content

Commit 18053a3

Browse files
author
Corey B
committed
feat(ui): orchestrate zero-js view-state routing and synchronize frontend libraries natively
1 parent d4c9afc commit 18053a3

3 files changed

Lines changed: 516 additions & 116 deletions

File tree

examples/personal_crm.fdd

Lines changed: 124 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,144 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8">
5-
<title>1-File CRM - OpenFDD</title>
5+
<title>Personal CRM - OpenFDD</title>
66
</head>
7-
<body style="background: #f1f5f9; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0;">
7+
<body style="background: #f1f5f9; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; font-family: sans-serif;">
88
<fdd-container extension=".fdd">
99
<script type="application/vc+json" id="fdd-data">
1010
{
11-
"issuer": "user-self",
12-
"credentialSubject": { "contactName": "Alice Smith", "company": "Tech Corp", "role": "VP Engineering" }
11+
"issuer": "did:web:sales.mycompany.com",
12+
"credentialSubject": {
13+
"title": "My Personal CRM Workspace",
14+
"lastUpdated": "2026-04-10"
15+
},
16+
"proof": { "signature": "demo_sig" }
1317
}
1418
</script>
1519
<script type="application/json" id="user-notes">
16-
{ "followUp": "Schedule a demo for next Tuesday.", "lastContacted": "2026-04-10" }
20+
{
21+
"contacts": [
22+
{"name": "Alice Johnson", "role": "VP Engineering", "company": "TechCorp", "email": "alice@techcorp.io", "notes": "Solid technical background. Discussed expanding their SaaS offering in Q2.", "archived": false},
23+
{"name": "Bob Smith", "role": "CEO", "company": "StartupX", "email": "bob@startupx.com", "notes": "Passionate founder. Not ready for seed series until next year.", "archived": true}
24+
],
25+
"globalScratchpad": "Need to follow up with Alice before Q3. Keep Bob archived for now."
26+
}
1727
</script>
1828
<template shadowrootmode="open" bind="#fdd-data, #user-notes">
1929
<style>
2030
:host {
21-
display: block; font-family: system-ui, sans-serif; width: 400px; background: white;
22-
border-radius: 16px; padding: 2rem; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1);
23-
}
24-
.notes-area {
25-
width: 100%; height: 120px; margin-top: 5px; box-sizing: border-box; border-radius: 8px;
26-
border: 1px solid #cbd5e1; padding: 10px; font-family: system-ui; resize: none;
31+
display: flex; width: 1000px; height: 600px; background: white;
32+
border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); overflow: hidden;
2733
}
28-
.notes-area:focus { outline: 2px solid #3b82f6; border-color: transparent; }
29-
.contact-info { border-bottom: 2px dashed #e2e8f0; padding-bottom: 1rem; margin-bottom: 1rem; }
30-
h2 { margin: 0; color: #0f172a; font-size: 1.5rem; }
31-
.role { color: #64748b; font-size: 1rem; margin-top: 5px; }
34+
.sidebar { width: 350px; background: #f8fafc; border-right: 1px solid #e2e8f0; display: flex; flex-direction: column; }
35+
.sidebar-header { padding: 20px; background: #0f172a; color: white; }
36+
.sidebar-header h2 { margin: 0 0 10px 0; font-size: 1.2rem; }
37+
.sidebar-content { flex: 1; overflow-y: auto; padding: 10px; }
38+
.search-bar { width: 100%; padding: 10px; border-radius: 6px; border: none; box-sizing: border-box; margin-bottom: 15px; }
39+
40+
.contact-card { background: white; padding: 15px; border-radius: 8px; margin-bottom: 10px; border: 1px solid #cbd5e1; box-shadow: 0 2px 4px rgba(0,0,0,0.02); }
41+
.contact-card h3 { margin: 0 0 5px 0; color: #1e293b; font-size: 1.1rem; display: flex; justify-content: space-between; align-items: center;}
42+
.contact-card p { margin: 0 0 10px 0; color: #64748b; font-size: 0.9rem; }
43+
.actions { display: flex; gap: 8px; margin-top: 15px;}
44+
button { cursor: pointer; padding: 6px 12px; border-radius: 4px; border: none; font-weight: bold; font-size: 0.8rem; }
45+
.btn-archive { background: #fef3c7; color: #d97706; }
46+
.btn-delete { background: #fee2e2; color: #dc2626; }
47+
.btn-unarchive { background: #dbeafe; color: #2563eb; }
48+
.btn-edit { background: #3b82f6; color: white; }
49+
50+
.main-panel { flex: 1; padding: 40px; background: #ffffff; overflow-y: auto; }
51+
.main-panel h2 { color: #0f172a; margin-top: 0; }
52+
53+
form { display: flex; flex-direction: column; gap: 15px; max-width: 400px; margin-top: 20px; }
54+
input, textarea { padding: 10px; border: 1px solid #cbd5e1; border-radius: 6px; font-family: sans-serif; background: #fdfdfd;}
55+
textarea { resize: vertical; }
56+
.btn-submit { background: #10b981; color: white; padding: 12px; font-size: 1rem; }
57+
58+
.archived-badge { display: inline-block; background: #cbd5e1; color: #334155; padding: 2px 6px; border-radius: 4px; font-size: 0.7rem; vertical-align: middle;}
59+
60+
.field-group { display: flex; flex-direction: column; gap: 5px; margin-bottom: 15px; }
61+
.field-group label { font-size: 0.85rem; font-weight: bold; color: #64748b; text-transform: uppercase; letter-spacing: 0.5px;}
3262
</style>
33-
<div class="contact-info">
34-
<h2>{{contactName}}</h2>
35-
<div class="role"><strong>{{role}}</strong> at {{company}}</div>
36-
</div>
37-
<div>
38-
<label style="font-weight: bold; color: #475569; font-size: 0.9rem;">My Notes (Auto-saves to disk):</label>
39-
<textarea name="user-notes.followUp" class="notes-area" autosave>{{followUp}}</textarea>
63+
64+
<div class="sidebar">
65+
<div class="sidebar-header">
66+
<h2>{{title}}</h2>
67+
<input type="text" class="search-bar" placeholder="Search contacts..." data-filter-target=".contact-card" data-filter-attr="data-name">
68+
</div>
69+
<div class="sidebar-content">
70+
{{#each contacts}}
71+
<div class="contact-card" data-name="{{name}} {{company}}">
72+
<h3>
73+
<span>{{name}} <span class="archived-badge" fdd-if="{{archived}}">ARCHIVED</span></span>
74+
<!-- EPHEMERAL STATE UI TARGETING -->
75+
<button class="btn-edit" fdd-action="set-ui" fdd-target="selectedContact" fdd-value="{{@index}}">Edit</button>
76+
</h3>
77+
<p>{{role}} @ {{company}}</p>
78+
79+
<div class="actions">
80+
<button class="btn-archive" fdd-unless="{{archived}}" fdd-action="set" fdd-target="user-notes.contacts.{{@index}}.archived" fdd-value="true">Archive</button>
81+
<button class="btn-unarchive" fdd-if="{{archived}}" fdd-action="set" fdd-target="user-notes.contacts.{{@index}}.archived" fdd-value="false">Restore</button>
82+
<button class="btn-delete" fdd-action="delete" fdd-target="user-notes.contacts" fdd-index="{{@index}}">Delete</button>
83+
</div>
84+
</div>
85+
{{/each}}
86+
</div>
4087
</div>
41-
<div style="margin-top:1rem; font-size:0.85rem; color:#94a3b8; display:flex; justify-content:space-between;">
42-
<span>Last Contacted: {{lastContacted}}</span>
43-
<span style="color:#10b981; font-weight:bold;">1-File Sync Active</span>
88+
89+
<div class="main-panel">
90+
91+
<!-- FALLBACK/EMPTY STATE -> VISIBLE WHEN selectedContact is 'undefined' -->
92+
<div fdd-match="ui.selectedContact" fdd-value="undefined">
93+
<h2>Add New Contact</h2>
94+
<p style="color: #64748b;">This declarative form binds directly to your secure FDD Array via <code>fdd-push</code>. It requires zero JavaScript to execute!</p>
95+
96+
<form fdd-action="push" fdd-target="user-notes.contacts">
97+
<input type="text" name="name" placeholder="Full Name" required>
98+
<input type="text" name="role" placeholder="Job Title" required>
99+
<input type="text" name="company" placeholder="Company" required>
100+
<input type="email" name="email" placeholder="Email Address">
101+
<input type="hidden" name="archived" value="false">
102+
<button type="submit" class="btn-submit">+ Append to CRM</button>
103+
</form>
104+
105+
<div style="margin-top: 50px;">
106+
<h3 style="color: #0f172a; margin-bottom: 10px;">Workspace Notepad</h3>
107+
<textarea name="user-notes.globalScratchpad" autosave style="width: 100%; height: 100px;">{{globalScratchpad}}</textarea>
108+
</div>
109+
</div>
110+
111+
<!-- DEEP VIEW / ACTIVE EDIT RECORD -> VISIBLE ONLY WHEN ui.selectedContact == @index -->
112+
{{#each contacts}}
113+
<div fdd-match="ui.selectedContact" fdd-value="{{@index}}">
114+
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #e2e8f0; padding-bottom: 15px; margin-bottom: 25px;">
115+
<h2 style="margin: 0; color: #2563eb;">Editing: {{name}}</h2>
116+
<!-- Closes the deep view by erasing the ephemeral UI state explicitly -->
117+
<button fdd-action="set-ui" fdd-target="selectedContact" fdd-value="undefined" style="background: transparent; border: 1px solid #cbd5e1; color: #475569;">Close View</button>
118+
</div>
119+
120+
<div class="field-group">
121+
<label>Full Name</label>
122+
<input name="user-notes.contacts.{{@index}}.name" value="{{name}}" autosave>
123+
</div>
124+
<div class="field-group">
125+
<label>Professional Role</label>
126+
<input name="user-notes.contacts.{{@index}}.role" value="{{role}}" autosave>
127+
</div>
128+
<div class="field-group">
129+
<label>Company</label>
130+
<input name="user-notes.contacts.{{@index}}.company" value="{{company}}" autosave>
131+
</div>
132+
<div class="field-group">
133+
<label>Email Address</label>
134+
<input type="email" name="user-notes.contacts.{{@index}}.email" value="{{email}}" autosave>
135+
</div>
136+
<div class="field-group" style="margin-top: 15px;">
137+
<label>Private Notes</label>
138+
<textarea name="user-notes.contacts.{{@index}}.notes" autosave style="height: 150px;">{{notes}}</textarea>
139+
</div>
140+
</div>
141+
{{/each}}
142+
44143
</div>
45144
</template>
46145
</fdd-container>

0 commit comments

Comments
 (0)