11import { useState , useEffect , useMemo , useRef , useCallback } from 'react' ;
2- import type { UicBarcodeTicketInput , CurveName } from 'dosipas-ts' ;
2+ import type { UicBarcodeTicketInput , UicDynamicContentDataInput , CurveName } from 'dosipas-ts' ;
33import TicketForm , { ToggleSection , NumberField , OptionalNumberField } from './TicketForm' ;
44import KeyPairInput from './KeyPairInput' ;
55import AztecBarcode from './AztecBarcode' ;
@@ -192,7 +192,7 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
192192 if ( prefillInput ) {
193193 setInput ( prefillInput ) ;
194194 // Enable L2 if prefill has dynamic data
195- if ( prefillInput . dynamicData ) {
195+ if ( prefillInput . dynamicData || prefillInput . dynamicContentData ) {
196196 setL2Enabled ( true ) ;
197197 }
198198 // Populate signatures from prefilled input if present
@@ -292,7 +292,7 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
292292 setEncodeError ( null ) ;
293293
294294 // Start dynamic refresh countdown if enabled
295- if ( l2Enabled && dynamicRefreshEnabled && input . dynamicData && l1PrivKey && l2PrivKey ) {
295+ if ( l2Enabled && dynamicRefreshEnabled && ( input . dynamicData || input . dynamicContentData ) && l1PrivKey && l2PrivKey ) {
296296 regenRef . current = { input, l1PrivKey, l1Curve, l2PrivKey, l2Curve } ;
297297 setCountdown ( dynamicRefreshInterval ) ;
298298 } else {
@@ -328,14 +328,28 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
328328 const secondsSinceMidnight =
329329 now . getHours ( ) * 3600 + now . getMinutes ( ) * 60 + now . getSeconds ( ) ;
330330
331- const updatedInput : UicBarcodeTicketInput = {
332- ...params . input ,
333- dynamicData : {
334- ...params . input . dynamicData ! ,
335- dynamicContentDay : dayOfYear ,
336- dynamicContentTime : secondsSinceMidnight ,
337- } ,
338- } ;
331+ let updatedInput : UicBarcodeTicketInput ;
332+ if ( params . input . dynamicContentData ) {
333+ updatedInput = {
334+ ...params . input ,
335+ dynamicContentData : {
336+ ...params . input . dynamicContentData ,
337+ dynamicContentTimeStamp : {
338+ day : dayOfYear ,
339+ time : secondsSinceMidnight ,
340+ } ,
341+ } ,
342+ } ;
343+ } else {
344+ updatedInput = {
345+ ...params . input ,
346+ dynamicData : {
347+ ...params . input . dynamicData ! ,
348+ dynamicContentDay : dayOfYear ,
349+ dynamicContentTime : secondsSinceMidnight ,
350+ } ,
351+ } ;
352+ }
339353 regenRef . current = { ...params , input : updatedInput } ;
340354 setInput ( updatedInput ) ;
341355
@@ -397,11 +411,11 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
397411
398412 // Stop regeneration when L2 is disabled or dynamic refresh is turned off
399413 useEffect ( ( ) => {
400- if ( ! l2Enabled || ! dynamicRefreshEnabled || ! input . dynamicData ) {
414+ if ( ! l2Enabled || ! dynamicRefreshEnabled || ( ! input . dynamicData && ! input . dynamicContentData ) ) {
401415 regenRef . current = null ;
402416 setCountdown ( null ) ;
403417 }
404- } , [ l2Enabled , dynamicRefreshEnabled , input . dynamicData ] ) ;
418+ } , [ l2Enabled , dynamicRefreshEnabled , input . dynamicData , input . dynamicContentData ] ) ;
405419
406420 // -------------------------------------------------------------------------
407421 // Dynamic data update helper
@@ -431,7 +445,8 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
431445 navigator . clipboard . writeText ( jsonPreview ) ;
432446 } ;
433447
434- const hasDynamic = ! ! input . dynamicData ;
448+ const hasDynamic = ! ! input . dynamicData || ! ! input . dynamicContentData ;
449+ const dynamicFormat : 'intercode' | 'fdc1' = input . dynamicContentData ? 'fdc1' : 'intercode' ;
435450 const l2KeyPresent = l2Enabled && ! ! l2PrivKey ;
436451
437452 return (
@@ -511,43 +526,127 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
511526
512527 { /* Level 2 Dynamic Data */ }
513528 < ToggleSection
514- title = "Intercode 6 Dynamic Data"
529+ title = "Level 2 Dynamic Data"
515530 enabled = { hasDynamic }
516531 onToggle = { ( v ) => {
517- handleInputChange ( {
518- ...input ,
519- dynamicData : v
520- ? { rics : input . securityProviderNum ?? 0 , dynamicContentDay : 0 }
521- : undefined ,
522- } ) ;
532+ if ( v ) {
533+ handleInputChange ( {
534+ ...input ,
535+ dynamicContentData : { } ,
536+ dynamicData : undefined ,
537+ } ) ;
538+ } else {
539+ handleInputChange ( {
540+ ...input ,
541+ dynamicData : undefined ,
542+ dynamicContentData : undefined ,
543+ } ) ;
544+ }
523545 } }
524546 >
525- < NumberField
526- label = "RICS"
527- value = { input . dynamicData ?. rics }
528- onChange = { ( v ) => updateDynamicData ( { rics : v ?? 0 } ) }
529- placeholder = "e.g. 3703"
530- />
531- < NumberField
532- label = "Day"
533- value = { input . dynamicData ?. dynamicContentDay }
534- onChange = { ( v ) => updateDynamicData ( { dynamicContentDay : v } ) }
535- />
536- < OptionalNumberField
537- label = "Time"
538- value = { input . dynamicData ?. dynamicContentTime }
539- onChange = { ( v ) => updateDynamicData ( { dynamicContentTime : v } ) }
540- />
541- < OptionalNumberField
542- label = "UTC Offset"
543- value = { input . dynamicData ?. dynamicContentUTCOffset }
544- onChange = { ( v ) => updateDynamicData ( { dynamicContentUTCOffset : v } ) }
545- />
546- < OptionalNumberField
547- label = "Duration"
548- value = { input . dynamicData ?. dynamicContentDuration }
549- onChange = { ( v ) => updateDynamicData ( { dynamicContentDuration : v } ) }
550- />
547+ { /* Format selector */ }
548+ < div className = "col-span-2 flex items-center gap-3 mb-1" >
549+ < span className = "text-xs text-gray-500" > Format</ span >
550+ < label className = "flex items-center gap-1" >
551+ < input
552+ type = "radio"
553+ name = "dynamicFormat"
554+ checked = { dynamicFormat === 'fdc1' }
555+ onChange = { ( ) => {
556+ handleInputChange ( {
557+ ...input ,
558+ dynamicContentData : { } ,
559+ dynamicData : undefined ,
560+ } ) ;
561+ } }
562+ className = "text-blue-600"
563+ />
564+ < span className = "text-xs text-gray-700" > FDC1</ span >
565+ </ label >
566+ < label className = "flex items-center gap-1" >
567+ < input
568+ type = "radio"
569+ name = "dynamicFormat"
570+ checked = { dynamicFormat === 'intercode' }
571+ onChange = { ( ) => {
572+ handleInputChange ( {
573+ ...input ,
574+ dynamicData : { rics : input . securityProviderNum ?? 0 , dynamicContentDay : 0 } ,
575+ dynamicContentData : undefined ,
576+ } ) ;
577+ } }
578+ className = "text-blue-600"
579+ />
580+ < span className = "text-xs text-gray-700" > Intercode 6 (_RICS.ID1)</ span >
581+ </ label >
582+ </ div >
583+
584+ { dynamicFormat === 'fdc1' && (
585+ < >
586+ < NumberField
587+ label = "Timestamp Day"
588+ value = { input . dynamicContentData ?. dynamicContentTimeStamp ?. day }
589+ onChange = { ( v ) => {
590+ const ts = input . dynamicContentData ?. dynamicContentTimeStamp ;
591+ handleInputChange ( {
592+ ...input ,
593+ dynamicContentData : {
594+ ...input . dynamicContentData ,
595+ dynamicContentTimeStamp : v != null ? { day : v , time : ts ?. time ?? 0 } : undefined ,
596+ } ,
597+ } ) ;
598+ } }
599+ placeholder = "Day of year (1-366)"
600+ />
601+ < NumberField
602+ label = "Timestamp Time"
603+ value = { input . dynamicContentData ?. dynamicContentTimeStamp ?. time }
604+ onChange = { ( v ) => {
605+ const ts = input . dynamicContentData ?. dynamicContentTimeStamp ;
606+ handleInputChange ( {
607+ ...input ,
608+ dynamicContentData : {
609+ ...input . dynamicContentData ,
610+ dynamicContentTimeStamp : v != null ? { day : ts ?. day ?? 1 , time : v } : undefined ,
611+ } ,
612+ } ) ;
613+ } }
614+ placeholder = "Seconds since midnight (0-86399)"
615+ />
616+ </ >
617+ ) }
618+
619+ { dynamicFormat === 'intercode' && (
620+ < >
621+ < NumberField
622+ label = "RICS"
623+ value = { input . dynamicData ?. rics }
624+ onChange = { ( v ) => updateDynamicData ( { rics : v ?? 0 } ) }
625+ placeholder = "e.g. 3703"
626+ />
627+ < NumberField
628+ label = "Day"
629+ value = { input . dynamicData ?. dynamicContentDay }
630+ onChange = { ( v ) => updateDynamicData ( { dynamicContentDay : v } ) }
631+ />
632+ < OptionalNumberField
633+ label = "Time"
634+ value = { input . dynamicData ?. dynamicContentTime }
635+ onChange = { ( v ) => updateDynamicData ( { dynamicContentTime : v } ) }
636+ />
637+ < OptionalNumberField
638+ label = "UTC Offset"
639+ value = { input . dynamicData ?. dynamicContentUTCOffset }
640+ onChange = { ( v ) => updateDynamicData ( { dynamicContentUTCOffset : v } ) }
641+ />
642+ < OptionalNumberField
643+ label = "Duration"
644+ value = { input . dynamicData ?. dynamicContentDuration }
645+ onChange = { ( v ) => updateDynamicData ( { dynamicContentDuration : v } ) }
646+ />
647+ </ >
648+ ) }
649+
551650 { l2KeyPresent && (
552651 < div className = "col-span-2 space-y-2 border-t border-gray-100 pt-2 mt-1" >
553652 < label className = "flex items-center gap-2" >
@@ -558,7 +657,7 @@ export default function EncodeTab({ onDecode, prefillInput, onPrefillConsumed }:
558657 className = "rounded border-gray-300"
559658 />
560659 < span className = "text-xs text-gray-600" >
561- Refresh dynamicContentDate and dynamicContentTime
660+ Refresh timestamp automatically
562661 </ span >
563662 </ label >
564663 { dynamicRefreshEnabled && (
0 commit comments