raidAthmaneBenlala commited on
Commit
76265b8
·
1 Parent(s): 03a23e0

✨ Implement Interactive Dental Odontogram FDI Chart

Browse files
Files changed (1) hide show
  1. toothmap_frontend/src/App.jsx +48 -0
toothmap_frontend/src/App.jsx CHANGED
@@ -16,6 +16,9 @@ const MODES = [
16
  // Distinct colours for FDI quadrants 1-4
17
  const QUADRANT_COLOURS = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444'];
18
 
 
 
 
19
  function getColour(fdi) {
20
  if (!fdi || fdi < 0) return '#a855f7';
21
  const quad = Math.floor(fdi / 10) - 1;
@@ -230,6 +233,51 @@ export default function App() {
230
  )}
231
  </div>
232
  )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  </aside>
234
 
235
  {/* Canvas */}
 
16
  // Distinct colours for FDI quadrants 1-4
17
  const QUADRANT_COLOURS = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444'];
18
 
19
+ const UPPER_TEETH = [18, 17, 16, 15, 14, 13, 12, 11, 21, 22, 23, 24, 25, 26, 27, 28];
20
+ const LOWER_TEETH = [48, 47, 46, 45, 44, 43, 42, 41, 31, 32, 33, 34, 35, 36, 37, 38];
21
+
22
  function getColour(fdi) {
23
  if (!fdi || fdi < 0) return '#a855f7';
24
  const quad = Math.floor(fdi / 10) - 1;
 
233
  )}
234
  </div>
235
  )}
236
+
237
+ {/* Dental FDI Chart (Odontogram) */}
238
+ {(pipelineResults && mode.includes('pipeline')) && (
239
+ <div className="bg-zinc-900/60 rounded-xl p-3 border border-zinc-800/80 shadow-inner">
240
+ <p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-3 text-center">FDI Dental Chart</p>
241
+ <div className="flex flex-col gap-1.5">
242
+ {/* Upper Row */}
243
+ <div className="flex w-full justify-between gap-0.5">
244
+ {UPPER_TEETH.map((t, idx) => {
245
+ const isLeftQuad = idx < 8; // patient's right is visually on the left
246
+ return (
247
+ <div key={t}
248
+ className={`flex-1 aspect-[2/3] rounded-[2px] flex items-center justify-center text-[9px] font-bold transition-all ${
249
+ pipelineResults.some(r => r.fdi === t)
250
+ ? 'bg-green-500/20 text-green-400 border border-green-500/40 shadow-[0_0_8px_rgba(34,197,94,0.15)]'
251
+ : 'bg-zinc-800 text-zinc-600 border border-zinc-700/40 opacity-70'
252
+ } ${isLeftQuad && idx === 7 ? 'mr-1.5' : ''}`}>
253
+ {t}
254
+ </div>
255
+ )})}
256
+ </div>
257
+ {/* Divider */}
258
+ <div className="h-px w-full bg-zinc-800/80" />
259
+ {/* Lower Row */}
260
+ <div className="flex w-full justify-between gap-0.5">
261
+ {LOWER_TEETH.map((t, idx) => {
262
+ const isLeftQuad = idx < 8;
263
+ return (
264
+ <div key={t}
265
+ className={`flex-1 aspect-[2/3] rounded-[2px] flex items-center justify-center text-[9px] font-bold transition-all ${
266
+ pipelineResults.some(r => r.fdi === t)
267
+ ? 'bg-green-500/20 text-green-400 border border-green-500/40 shadow-[0_0_8px_rgba(34,197,94,0.15)]'
268
+ : 'bg-zinc-800 text-zinc-600 border border-zinc-700/40 opacity-70'
269
+ } ${isLeftQuad && idx === 7 ? 'mr-1.5' : ''}`}>
270
+ {t}
271
+ </div>
272
+ )})}
273
+ </div>
274
+ </div>
275
+ <div className="flex justify-center gap-4 mt-3 text-[10px] text-zinc-500">
276
+ <div className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-sm bg-green-500/20 border border-green-500/40"></span> Present</div>
277
+ <div className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-sm bg-zinc-800 border border-zinc-700/40"></span> Missing</div>
278
+ </div>
279
+ </div>
280
+ )}
281
  </aside>
282
 
283
  {/* Canvas */}