Diff -- /var/www/vhosts/3dshawn.com/ptmatrix.3dshawn.com/TEMPLATES/tf_preferences.html
Diff

/var/www/vhosts/3dshawn.com/ptmatrix.3dshawn.com/TEMPLATES/tf_preferences.html

added on local at 2026-07-01 12:35:04

Added
+197
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 9f44fbd934f7
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1<div class="page-header"><div><h1>Preferences</h1><p class="subtitle">Tune PTMatrix to your workflow.</p></div></div>
2[if:$has_flash]<div class="flash-msg">$flash</div>[/if]
3<form method="POST">
4 <div class="row" style="gap:24px;flex-wrap:wrap">
5 <div class="col" style="min-width:280px;flex:1">
6 <div class="card">
7 <div class="card-header"><h3>Appearance</h3></div>
8 <div class="field"><label>Theme</label>
9 <select class="select" name="theme">
10 <option value="dark"[if:$theme_dark] selected[/if]>Dark</option>
11 <option value="light"[if:$theme_light] selected[/if]>Light (coming soon)</option>
12 <option value="auto"[if:$theme_auto] selected[/if]>Match system</option>
13 </select>
14 </div>
15 <div class="field"><label>Default project view</label>
16 <select class="select" name="default_view">
17 <option value="kanban"[if:$view_kanban] selected[/if]>Kanban board</option>
18 <option value="gantt"[if:$view_gantt] selected[/if]>Gantt chart</option>
19 <option value="list"[if:$view_list] selected[/if]>List</option>
20 <option value="calendar"[if:$view_cal] selected[/if]>Calendar</option>
21 </select>
22 </div>
23 <label class="checkbox" style="display:flex;padding:8px 0" data-tip="When ON, hovering field labels and icons reveals styled hint popovers. Once you know your way around, turn this off for a calmer interface.">
24 <input type="checkbox" name="show_tooltips" value="1"[if:$show_tips] checked[/if]>
25 Show interface helpers (hover tooltips)
26 </label>
27 </div>
28 </div>
29
30 <div class="col" style="min-width:300px;flex:2">
31 <div class="card">
32 <div class="card-header"><h3>Board appearance</h3></div>
33 <div class="field">
34 <label data-tip="Pick how the background of Kanban cards is tinted. Priority lets each card's gradient reflect urgency. Assignee uses each owner's avatar color. Custom lets you pick a fixed pair of colors.">Card background</label>
35 <div class="mode-radios">
36 <label>
37 <input type="radio" name="card_gradient_mode" value="off"[if:$grad_off] checked[/if]>
38 <span class="mode-swatch" style="background:var(--col-surface)"></span>
39 <span>Flat</span>
40 </label>
41 <label>
42 <input type="radio" name="card_gradient_mode" value="priority"[if:$grad_priority] checked[/if]>
43 <span class="mode-swatch" style="background:linear-gradient(135deg,rgba(245,158,11,.4),var(--col-surface))"></span>
44 <span>By priority</span>
45 </label>
46 <label>
47 <input type="radio" name="card_gradient_mode" value="assignee"[if:$grad_assignee] checked[/if]>
48 <span class="mode-swatch" style="background:linear-gradient(135deg,rgba(139,92,246,.4),var(--col-surface))"></span>
49 <span>By assignee</span>
50 </label>
51 <label>
52 <input type="radio" name="card_gradient_mode" value="custom"[if:$grad_custom] checked[/if]>
53 <span class="mode-swatch" id="customSwatch" style="background:linear-gradient(135deg,$grad_from,$grad_to)"></span>
54 <span>Custom pair</span>
55 </label>
56 </div>
57 </div>
58
59 <div class="row" style="gap:14px;align-items:end">
60 <div class="col" style="max-width:160px"><div class="field"><label>Custom from</label><input class="input" type="color" name="card_gradient_from" id="cgFrom" value="$grad_from" style="height:38px;padding:2px"></div></div>
61 <div class="col" style="max-width:160px"><div class="field"><label>Custom to</label><input class="input" type="color" name="card_gradient_to" id="cgTo" value="$grad_to" style="height:38px;padding:2px"></div></div>
62 <div class="col"><div class="grad-preview" id="gradPreview" style="background:linear-gradient(135deg,$grad_from,$grad_to)"></div></div>
63 </div>
64
65 <div class="field" style="margin-top:16px">
66 <label>Board density</label>
67 <select class="select" name="board_density">
68 <option value="compact"[if:$den_compact] selected[/if]>Compact (more on screen)</option>
69 <option value="comfortable"[if:$den_comfortable] selected[/if]>Comfortable (default)</option>
70 <option value="spacious"[if:$den_spacious] selected[/if]>Spacious (larger cards)</option>
71 </select>
72 </div>
73 </div>
74 </div>
75
76 <div class="col" style="min-width:280px;flex:2">
77 <div class="card">
78 <div class="card-header"><h3>Email notifications</h3></div>
79 <p class="text-muted" style="font-size:13px">Send me an email when&hellip;</p>
80 <div class="mt-1">
81 <label class="checkbox" style="display:flex;padding:8px 0"><input type="checkbox" name="email_notif_assigned" value="1"[if:$n_assigned] checked[/if]> A task is assigned to me</label>
82 <label class="checkbox" style="display:flex;padding:8px 0"><input type="checkbox" name="email_notif_mentioned" value="1"[if:$n_mentioned] checked[/if]> Someone &#64;-mentions me</label>
83 <label class="checkbox" style="display:flex;padding:8px 0"><input type="checkbox" name="email_notif_status" value="1"[if:$n_status] checked[/if]> Status changes on a task I'm watching</label>
84 <label class="checkbox" style="display:flex;padding:8px 0"><input type="checkbox" name="email_notif_due_soon" value="1"[if:$n_due_soon] checked[/if]> A task assigned to me is due in 24h</label>
85 <label class="checkbox" style="display:flex;padding:8px 0"><input type="checkbox" name="email_notif_weekly" value="1"[if:$n_weekly] checked[/if]> Weekly digest (Mondays)</label>
86 </div>
87 </div>
88
89 <div class="card mt-3" id="tf-push-card">
90 <div class="card-header"><h3>Browser push notifications</h3></div>
91 <p class="text-muted" style="font-size:13px;margin-bottom:14px">Get a system notification when you&rsquo;re mentioned, assigned a task, or have an SLA-affected ticket update &mdash; even when PTMatrix isn&rsquo;t open. Works on desktop and Android. On iOS, add PTMatrix to your home screen first.</p>
92 <div id="tf-push-status" style="font-size:13px;color:var(--col-text-dim);margin-bottom:12px">Checking browser support&hellip;</div>
93 <div style="display:flex;gap:8px;flex-wrap:wrap">
94 <button type="button" class="btn btn-primary" id="tf-push-enable" style="display:none">Enable on this device</button>
95 <button type="button" class="btn" id="tf-push-disable" style="display:none">Disable on this device</button>
96 <button type="button" class="btn" id="tf-push-test" style="display:none">Send test notification</button>
97 </div>
98 <div id="tf-push-err" style="display:none;color:#ef4444;font-size:12.5px;margin-top:10px"></div>
99 </div>
100 </div>
101 </div>
102 <div class="flex-end mt-3"><button type="submit" class="btn btn-primary">Save preferences</button></div>
103</form>
104
105<script>
106(function(){
107 var card = document.getElementById('tf-push-card');
108 if (!card) return;
109 var statusEl = document.getElementById('tf-push-status');
110 var btnOn = document.getElementById('tf-push-enable');
111 var btnOff = document.getElementById('tf-push-disable');
112 var btnTest = document.getElementById('tf-push-test');
113 var errEl = document.getElementById('tf-push-err');
114 function showErr(m){ errEl.style.display = 'block'; errEl.textContent = m; }
115 function clearErr(){ errEl.style.display = 'none'; errEl.textContent = ''; }
116
117 // tfPush is defined by the wrapper's inline script which loads after this
118 // body section. Wait up to ~2s for it before deciding.
119 var tries = 0;
120 (function waitForTfPush(){
121 if (window.tfPush) { initPush(); return; }
122 if (++tries > 40) {
123 statusEl.textContent = 'This browser does not support web push notifications.';
124 return;
125 }
126 setTimeout(waitForTfPush, 50);
127 })();
128
129 function initPush(){
130 if (!window.tfPush.supported) {
131 statusEl.textContent = 'This browser does not support web push notifications.';
132 return;
133 }
134 function refresh(){
135 var perm = tfPush.permission();
136 tfPush.isSubscribed().then(function(sub){
137 if (perm === 'denied') {
138 statusEl.textContent = 'Blocked: re-allow notifications in your browser site settings.';
139 btnOn.style.display = btnOff.style.display = btnTest.style.display = 'none';
140 } else if (sub) {
141 statusEl.innerHTML = '<span style="color:#22c55e;font-weight:600">✓ Subscribed on this device.</span>';
142 btnOn.style.display = 'none';
143 btnOff.style.display = 'inline-flex';
144 btnTest.style.display = 'inline-flex';
145 } else {
146 statusEl.textContent = 'Not subscribed on this device.';
147 btnOn.style.display = 'inline-flex';
148 btnOff.style.display = btnTest.style.display = 'none';
149 }
150 });
151 }
152 refresh();
153
154 btnOn.addEventListener('click', function(){
155 clearErr();
156 btnOn.disabled = true; btnOn.textContent = 'Asking…';
157 tfPush.enable().then(function(){ refresh(); }, function(err){
158 showErr(err && err.message ? err.message : 'Could not enable.');
159 }).then(function(){ btnOn.disabled = false; btnOn.textContent = 'Enable on this device'; });
160 });
161 btnOff.addEventListener('click', function(){
162 clearErr();
163 btnOff.disabled = true;
164 tfPush.disable().then(refresh).then(function(){ btnOff.disabled = false; }, function(err){
165 showErr(err && err.message ? err.message : 'Could not disable.');
166 btnOff.disabled = false;
167 });
168 });
169 btnTest.addEventListener('click', function(){
170 clearErr();
171 btnTest.disabled = true;
172 tfPush.test().then(function(){
173 btnTest.disabled = false;
174 statusEl.innerHTML = '<span style="color:#22c55e;font-weight:600">✓ Test push sent. Check your system notifications.</span>';
175 }, function(err){
176 btnTest.disabled = false;
177 showErr(err && err.message ? err.message : 'Test send failed.');
178 });
179 });
180 } // end initPush
181})();
182</script>
183
184<script>
185(function(){
186 var f = document.getElementById('cgFrom');
187 var t = document.getElementById('cgTo');
188 var p = document.getElementById('gradPreview');
189 var s = document.getElementById('customSwatch');
190 function paint(){
191 var grad = 'linear-gradient(135deg,' + f.value + ',' + t.value + ')';
192 if (p) p.style.background = grad;
193 if (s) s.style.background = grad;
194 }
195 if (f && t) { f.addEventListener('input', paint); t.addEventListener('input', paint); }
196})();
197</script>
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help