@atlashub/smartstack-cli 4.23.0 → 4.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/skills/ba-generate-html/html/ba-interactive.html +950 -1055
- package/templates/skills/ba-generate-html/html/src/scripts/01-data-init.js +1 -2
- package/templates/skills/ba-generate-html/html/src/scripts/02-navigation.js +0 -1
- package/templates/skills/ba-generate-html/html/src/scripts/03-render-cadrage.js +0 -39
- package/templates/skills/ba-generate-html/html/src/scripts/05-render-specs.js +0 -1
- package/templates/skills/ba-generate-html/html/src/scripts/07-render-handoff.js +0 -1
- package/templates/skills/ba-generate-html/html/src/scripts/08-editing.js +133 -135
- package/templates/skills/ba-generate-html/html/src/scripts/10-comments.js +199 -199
- package/templates/skills/ba-generate-html/html/src/scripts/11-review-panel.js +165 -166
- package/templates/skills/ba-generate-html/html/src/styles/05-modules.css +444 -454
- package/templates/skills/ba-generate-html/html/src/template.html +0 -49
- package/templates/skills/ba-generate-html/references/data-build.md +176 -182
- package/templates/skills/ba-generate-html/references/data-mapping.md +295 -301
- package/templates/skills/ba-generate-html/steps/step-01-collect.md +1 -1
- package/templates/skills/ba-generate-html/steps/step-02-build-data.md +0 -9
- package/templates/skills/derive-prd/SKILL.md +9 -9
- package/templates/skills/derive-prd/references/acceptance-criteria.md +166 -116
- package/templates/skills/derive-prd/references/entity-domain-mapping.md +5 -5
- package/templates/skills/derive-prd/references/handoff-file-templates.md +12 -12
- package/templates/skills/derive-prd/references/handoff-mappings.md +13 -14
- package/templates/skills/derive-prd/references/handoff-seeddata-generation.md +1 -1
- package/templates/skills/derive-prd/references/readiness-scoring.md +41 -50
- package/templates/skills/derive-prd/schemas/handoff-schema.json +2 -2
- package/templates/skills/derive-prd/steps/step-00-validate.md +73 -52
- package/templates/skills/derive-prd/steps/step-01-transform.md +86 -43
- package/templates/skills/ba-generate-html/html/src/partials/cadrage-risks.html +0 -48
|
@@ -473,460 +473,450 @@ body {
|
|
|
473
473
|
|
|
474
474
|
|
|
475
475
|
/* --- 05-modules.css --- */
|
|
476
|
-
/* ============================================
|
|
477
|
-
USE CASE LIST
|
|
478
|
-
============================================ */
|
|
479
|
-
.uc-list { list-style: none; }
|
|
480
|
-
|
|
481
|
-
.uc-item {
|
|
482
|
-
background: var(--bg-card);
|
|
483
|
-
border: 1px solid var(--border);
|
|
484
|
-
border-radius: 8px;
|
|
485
|
-
padding: 1rem 1.25rem;
|
|
486
|
-
margin-bottom: 0.75rem;
|
|
487
|
-
transition: border-color var(--transition-fast);
|
|
488
|
-
}
|
|
489
|
-
.uc-item:hover { border-color: var(--border-light); }
|
|
490
|
-
|
|
491
|
-
.uc-header {
|
|
492
|
-
display: flex;
|
|
493
|
-
align-items: center;
|
|
494
|
-
gap: 0.75rem;
|
|
495
|
-
margin-bottom: 0.5rem;
|
|
496
|
-
}
|
|
497
|
-
.uc-id {
|
|
498
|
-
font-size: 0.7rem;
|
|
499
|
-
font-weight: 700;
|
|
500
|
-
color: var(--primary-light);
|
|
501
|
-
background: rgba(99,102,241,0.1);
|
|
502
|
-
padding: 0.15rem 0.5rem;
|
|
503
|
-
border-radius: 4px;
|
|
504
|
-
}
|
|
505
|
-
.uc-title { font-weight: 600; color: var(--text-bright); flex: 1; }
|
|
506
|
-
.uc-actions { display: flex; gap: 0.3rem; opacity: 0; transition: opacity var(--transition-fast); }
|
|
507
|
-
.uc-item:hover .uc-actions { opacity: 1; }
|
|
508
|
-
|
|
509
|
-
.uc-detail { font-size: 0.875rem; color: var(--text); }
|
|
510
|
-
.uc-detail-label { color: var(--text-muted); font-size: 0.75rem; font-weight: 600; margin-top: 0.5rem; }
|
|
511
|
-
|
|
512
|
-
.uc-actors {
|
|
513
|
-
display: flex; gap: 0.4rem; margin-top: 0.3rem; flex-wrap: wrap;
|
|
514
|
-
}
|
|
515
|
-
.uc-actor {
|
|
516
|
-
font-size: 0.7rem;
|
|
517
|
-
padding: 0.1rem 0.4rem;
|
|
518
|
-
background: rgba(6,182,212,0.1);
|
|
519
|
-
color: var(--accent);
|
|
520
|
-
border-radius: 4px;
|
|
521
|
-
border: 1px solid rgba(6,182,212,0.2);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/* ============================================
|
|
525
|
-
BUSINESS RULE LIST
|
|
526
|
-
============================================ */
|
|
527
|
-
.br-item {
|
|
528
|
-
display: flex;
|
|
529
|
-
align-items: flex-start;
|
|
530
|
-
gap: 0.75rem;
|
|
531
|
-
padding: 0.75rem 1rem;
|
|
532
|
-
background: var(--bg-card);
|
|
533
|
-
border: 1px solid var(--border);
|
|
534
|
-
border-radius: 8px;
|
|
535
|
-
margin-bottom: 0.5rem;
|
|
536
|
-
}
|
|
537
|
-
.br-category {
|
|
538
|
-
font-size: 0.65rem;
|
|
539
|
-
font-weight: 700;
|
|
540
|
-
text-transform: uppercase;
|
|
541
|
-
padding: 0.15rem 0.4rem;
|
|
542
|
-
border-radius: 4px;
|
|
543
|
-
flex-shrink: 0;
|
|
544
|
-
min-width: 70px;
|
|
545
|
-
text-align: center;
|
|
546
|
-
}
|
|
547
|
-
.br-cat-validation { background: rgba(99,102,241,0.15); color: var(--primary-light); }
|
|
548
|
-
.br-cat-calculation { background: rgba(234,179,8,0.15); color: #facc15; }
|
|
549
|
-
.br-cat-workflow { background: rgba(249,115,22,0.15); color: var(--secondary); }
|
|
550
|
-
.br-cat-security { background: rgba(239,68,68,0.15); color: #f87171; }
|
|
551
|
-
.br-cat-data { background: rgba(6,182,212,0.15); color: var(--accent); }
|
|
552
|
-
.br-text { flex: 1; font-size: 0.875rem; }
|
|
553
|
-
|
|
554
|
-
/* ============================================
|
|
555
|
-
STAKEHOLDER TABLE
|
|
556
|
-
============================================ */
|
|
557
|
-
.stakeholder-grid {
|
|
558
|
-
display: grid;
|
|
559
|
-
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
560
|
-
gap: 1rem;
|
|
561
|
-
}
|
|
562
|
-
.stakeholder-card {
|
|
563
|
-
background: var(--bg-card);
|
|
564
|
-
border: 1px solid var(--border);
|
|
565
|
-
border-radius: 10px;
|
|
566
|
-
padding: 1rem;
|
|
567
|
-
}
|
|
568
|
-
.stakeholder-card:hover { border-color: var(--border-light); }
|
|
569
|
-
.stakeholder-role { font-weight: 600; color: var(--text-bright); margin-bottom: 0.25rem; }
|
|
570
|
-
.stakeholder-function { font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.5rem; }
|
|
571
|
-
.stakeholder-tasks { list-style: none; }
|
|
572
|
-
.stakeholder-tasks li {
|
|
573
|
-
font-size: 0.8rem;
|
|
574
|
-
padding: 0.15rem 0;
|
|
575
|
-
color: var(--text);
|
|
576
|
-
}
|
|
577
|
-
.stakeholder-tasks li::before { content: "- "; color: var(--primary-light); }
|
|
578
|
-
.stakeholder-meta {
|
|
579
|
-
display: flex; gap: 0.75rem; margin-top: 0.5rem; padding-top: 0.5rem;
|
|
580
|
-
border-top: 1px solid var(--border);
|
|
581
|
-
}
|
|
582
|
-
.stakeholder-meta span { font-size: 0.7rem; color: var(--text-muted); }
|
|
583
|
-
|
|
584
|
-
/* ============================================
|
|
585
|
-
PROCESS FLOW
|
|
586
|
-
============================================ */
|
|
587
|
-
.process-flow {
|
|
588
|
-
display: flex;
|
|
589
|
-
align-items: center;
|
|
590
|
-
gap: 0.25rem;
|
|
591
|
-
overflow-x: auto;
|
|
592
|
-
padding: 1rem 0;
|
|
593
|
-
}
|
|
594
|
-
.process-step {
|
|
595
|
-
background: var(--bg-card);
|
|
596
|
-
border: 1px solid var(--border);
|
|
597
|
-
border-radius: 8px;
|
|
598
|
-
padding: 0.75rem 1rem;
|
|
599
|
-
min-width: 140px;
|
|
600
|
-
text-align: center;
|
|
601
|
-
flex-shrink: 0;
|
|
602
|
-
}
|
|
603
|
-
.process-step:hover { border-color: var(--primary); }
|
|
604
|
-
.process-step-number {
|
|
605
|
-
font-size: 0.65rem;
|
|
606
|
-
color: var(--primary-light);
|
|
607
|
-
font-weight: 700;
|
|
608
|
-
}
|
|
609
|
-
.process-step-label { font-size: 0.8rem; color: var(--text-bright); font-weight: 500; }
|
|
610
|
-
.process-arrow { color: var(--text-muted); font-size: 1.2rem; flex-shrink: 0; }
|
|
611
|
-
|
|
612
|
-
/* ============================================
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
.
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
.module-card {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
.module-card
|
|
658
|
-
.module-card
|
|
659
|
-
.module-card-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
font-size: 0.
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
}
|
|
666
|
-
.module-card-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
.
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
.attr-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
padding:
|
|
731
|
-
border-
|
|
732
|
-
}
|
|
733
|
-
.
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
.
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
.
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
.dep-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
.
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
.
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
.
|
|
785
|
-
.
|
|
786
|
-
font-size: 0.
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
}
|
|
790
|
-
.e2e-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
background: var(--bg-card);
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
.
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
.dm-
|
|
811
|
-
.dm-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
.dm-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
border-
|
|
818
|
-
}
|
|
819
|
-
.dm-
|
|
820
|
-
.dm-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
.dm-entity-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
835
|
-
.dm-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}
|
|
840
|
-
.dm-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
.
|
|
845
|
-
.
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
.dm-
|
|
852
|
-
|
|
853
|
-
.
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
margin-bottom:
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
.
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
border-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
.struct-section-
|
|
886
|
-
font-
|
|
887
|
-
color: var(--text-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
color: var(--
|
|
919
|
-
|
|
920
|
-
flex-shrink: 0;
|
|
921
|
-
}
|
|
922
|
-
.struct-resource-name {
|
|
923
|
-
font-weight: 500;
|
|
924
|
-
color: var(--text-bright);
|
|
925
|
-
}
|
|
926
|
-
.struct-resource-desc {
|
|
927
|
-
font-size: 0.8rem;
|
|
928
|
-
color: var(--text-muted);
|
|
929
|
-
}
|
|
476
|
+
/* ============================================
|
|
477
|
+
USE CASE LIST
|
|
478
|
+
============================================ */
|
|
479
|
+
.uc-list { list-style: none; }
|
|
480
|
+
|
|
481
|
+
.uc-item {
|
|
482
|
+
background: var(--bg-card);
|
|
483
|
+
border: 1px solid var(--border);
|
|
484
|
+
border-radius: 8px;
|
|
485
|
+
padding: 1rem 1.25rem;
|
|
486
|
+
margin-bottom: 0.75rem;
|
|
487
|
+
transition: border-color var(--transition-fast);
|
|
488
|
+
}
|
|
489
|
+
.uc-item:hover { border-color: var(--border-light); }
|
|
490
|
+
|
|
491
|
+
.uc-header {
|
|
492
|
+
display: flex;
|
|
493
|
+
align-items: center;
|
|
494
|
+
gap: 0.75rem;
|
|
495
|
+
margin-bottom: 0.5rem;
|
|
496
|
+
}
|
|
497
|
+
.uc-id {
|
|
498
|
+
font-size: 0.7rem;
|
|
499
|
+
font-weight: 700;
|
|
500
|
+
color: var(--primary-light);
|
|
501
|
+
background: rgba(99,102,241,0.1);
|
|
502
|
+
padding: 0.15rem 0.5rem;
|
|
503
|
+
border-radius: 4px;
|
|
504
|
+
}
|
|
505
|
+
.uc-title { font-weight: 600; color: var(--text-bright); flex: 1; }
|
|
506
|
+
.uc-actions { display: flex; gap: 0.3rem; opacity: 0; transition: opacity var(--transition-fast); }
|
|
507
|
+
.uc-item:hover .uc-actions { opacity: 1; }
|
|
508
|
+
|
|
509
|
+
.uc-detail { font-size: 0.875rem; color: var(--text); }
|
|
510
|
+
.uc-detail-label { color: var(--text-muted); font-size: 0.75rem; font-weight: 600; margin-top: 0.5rem; }
|
|
511
|
+
|
|
512
|
+
.uc-actors {
|
|
513
|
+
display: flex; gap: 0.4rem; margin-top: 0.3rem; flex-wrap: wrap;
|
|
514
|
+
}
|
|
515
|
+
.uc-actor {
|
|
516
|
+
font-size: 0.7rem;
|
|
517
|
+
padding: 0.1rem 0.4rem;
|
|
518
|
+
background: rgba(6,182,212,0.1);
|
|
519
|
+
color: var(--accent);
|
|
520
|
+
border-radius: 4px;
|
|
521
|
+
border: 1px solid rgba(6,182,212,0.2);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/* ============================================
|
|
525
|
+
BUSINESS RULE LIST
|
|
526
|
+
============================================ */
|
|
527
|
+
.br-item {
|
|
528
|
+
display: flex;
|
|
529
|
+
align-items: flex-start;
|
|
530
|
+
gap: 0.75rem;
|
|
531
|
+
padding: 0.75rem 1rem;
|
|
532
|
+
background: var(--bg-card);
|
|
533
|
+
border: 1px solid var(--border);
|
|
534
|
+
border-radius: 8px;
|
|
535
|
+
margin-bottom: 0.5rem;
|
|
536
|
+
}
|
|
537
|
+
.br-category {
|
|
538
|
+
font-size: 0.65rem;
|
|
539
|
+
font-weight: 700;
|
|
540
|
+
text-transform: uppercase;
|
|
541
|
+
padding: 0.15rem 0.4rem;
|
|
542
|
+
border-radius: 4px;
|
|
543
|
+
flex-shrink: 0;
|
|
544
|
+
min-width: 70px;
|
|
545
|
+
text-align: center;
|
|
546
|
+
}
|
|
547
|
+
.br-cat-validation { background: rgba(99,102,241,0.15); color: var(--primary-light); }
|
|
548
|
+
.br-cat-calculation { background: rgba(234,179,8,0.15); color: #facc15; }
|
|
549
|
+
.br-cat-workflow { background: rgba(249,115,22,0.15); color: var(--secondary); }
|
|
550
|
+
.br-cat-security { background: rgba(239,68,68,0.15); color: #f87171; }
|
|
551
|
+
.br-cat-data { background: rgba(6,182,212,0.15); color: var(--accent); }
|
|
552
|
+
.br-text { flex: 1; font-size: 0.875rem; }
|
|
553
|
+
|
|
554
|
+
/* ============================================
|
|
555
|
+
STAKEHOLDER TABLE
|
|
556
|
+
============================================ */
|
|
557
|
+
.stakeholder-grid {
|
|
558
|
+
display: grid;
|
|
559
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
560
|
+
gap: 1rem;
|
|
561
|
+
}
|
|
562
|
+
.stakeholder-card {
|
|
563
|
+
background: var(--bg-card);
|
|
564
|
+
border: 1px solid var(--border);
|
|
565
|
+
border-radius: 10px;
|
|
566
|
+
padding: 1rem;
|
|
567
|
+
}
|
|
568
|
+
.stakeholder-card:hover { border-color: var(--border-light); }
|
|
569
|
+
.stakeholder-role { font-weight: 600; color: var(--text-bright); margin-bottom: 0.25rem; }
|
|
570
|
+
.stakeholder-function { font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.5rem; }
|
|
571
|
+
.stakeholder-tasks { list-style: none; }
|
|
572
|
+
.stakeholder-tasks li {
|
|
573
|
+
font-size: 0.8rem;
|
|
574
|
+
padding: 0.15rem 0;
|
|
575
|
+
color: var(--text);
|
|
576
|
+
}
|
|
577
|
+
.stakeholder-tasks li::before { content: "- "; color: var(--primary-light); }
|
|
578
|
+
.stakeholder-meta {
|
|
579
|
+
display: flex; gap: 0.75rem; margin-top: 0.5rem; padding-top: 0.5rem;
|
|
580
|
+
border-top: 1px solid var(--border);
|
|
581
|
+
}
|
|
582
|
+
.stakeholder-meta span { font-size: 0.7rem; color: var(--text-muted); }
|
|
583
|
+
|
|
584
|
+
/* ============================================
|
|
585
|
+
PROCESS FLOW
|
|
586
|
+
============================================ */
|
|
587
|
+
.process-flow {
|
|
588
|
+
display: flex;
|
|
589
|
+
align-items: center;
|
|
590
|
+
gap: 0.25rem;
|
|
591
|
+
overflow-x: auto;
|
|
592
|
+
padding: 1rem 0;
|
|
593
|
+
}
|
|
594
|
+
.process-step {
|
|
595
|
+
background: var(--bg-card);
|
|
596
|
+
border: 1px solid var(--border);
|
|
597
|
+
border-radius: 8px;
|
|
598
|
+
padding: 0.75rem 1rem;
|
|
599
|
+
min-width: 140px;
|
|
600
|
+
text-align: center;
|
|
601
|
+
flex-shrink: 0;
|
|
602
|
+
}
|
|
603
|
+
.process-step:hover { border-color: var(--primary); }
|
|
604
|
+
.process-step-number {
|
|
605
|
+
font-size: 0.65rem;
|
|
606
|
+
color: var(--primary-light);
|
|
607
|
+
font-weight: 700;
|
|
608
|
+
}
|
|
609
|
+
.process-step-label { font-size: 0.8rem; color: var(--text-bright); font-weight: 500; }
|
|
610
|
+
.process-arrow { color: var(--text-muted); font-size: 1.2rem; flex-shrink: 0; }
|
|
611
|
+
|
|
612
|
+
/* ============================================
|
|
613
|
+
display: grid;
|
|
614
|
+
grid-template-columns: auto 1fr auto auto;
|
|
615
|
+
gap: 1rem;
|
|
616
|
+
align-items: center;
|
|
617
|
+
padding: 0.75rem 1rem;
|
|
618
|
+
background: var(--bg-card);
|
|
619
|
+
border: 1px solid var(--border);
|
|
620
|
+
border-radius: 8px;
|
|
621
|
+
margin-bottom: 0.5rem;
|
|
622
|
+
}
|
|
623
|
+
width: 10px; height: 10px;
|
|
624
|
+
border-radius: 50%;
|
|
625
|
+
}
|
|
626
|
+
font-size: 0.7rem;
|
|
627
|
+
color: var(--text-muted);
|
|
628
|
+
text-align: center;
|
|
629
|
+
}
|
|
630
|
+
/* ============================================
|
|
631
|
+
MODULE CARDS (Decomposition)
|
|
632
|
+
============================================ */
|
|
633
|
+
.module-grid {
|
|
634
|
+
display: grid;
|
|
635
|
+
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
636
|
+
gap: 1rem;
|
|
637
|
+
}
|
|
638
|
+
.module-card {
|
|
639
|
+
background: var(--bg-card);
|
|
640
|
+
border: 1px solid var(--border);
|
|
641
|
+
border-radius: 10px;
|
|
642
|
+
padding: 1rem;
|
|
643
|
+
cursor: pointer;
|
|
644
|
+
transition: all var(--transition-fast);
|
|
645
|
+
position: relative;
|
|
646
|
+
}
|
|
647
|
+
.module-card:hover { border-color: var(--primary); }
|
|
648
|
+
.module-card.selected { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(99,102,241,0.2); }
|
|
649
|
+
.module-card-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.5rem; }
|
|
650
|
+
.module-card-code { font-weight: 700; color: var(--text-bright); font-size: 1rem; }
|
|
651
|
+
.module-card-type {
|
|
652
|
+
font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.05em;
|
|
653
|
+
padding: 0.15rem 0.5rem; border-radius: 4px;
|
|
654
|
+
background: rgba(99,102,241,0.1); color: var(--primary-light);
|
|
655
|
+
}
|
|
656
|
+
.module-card-desc { font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.5rem; }
|
|
657
|
+
.module-card-meta { display: flex; gap: 0.75rem; font-size: 0.7rem; color: var(--text-muted); }
|
|
658
|
+
.module-card-meta span { display: flex; align-items: center; gap: 0.2rem; }
|
|
659
|
+
.module-card-remove {
|
|
660
|
+
position: absolute; top: 0.5rem; right: 0.5rem;
|
|
661
|
+
background: none; border: none; color: var(--text-muted);
|
|
662
|
+
cursor: pointer; font-size: 0.8rem; opacity: 0;
|
|
663
|
+
transition: opacity var(--transition-fast);
|
|
664
|
+
}
|
|
665
|
+
.module-card:hover .module-card-remove { opacity: 1; }
|
|
666
|
+
.module-card-remove:hover { color: var(--error); }
|
|
667
|
+
|
|
668
|
+
/* ============================================
|
|
669
|
+
TABS (Module Specification)
|
|
670
|
+
============================================ */
|
|
671
|
+
.tab-bar {
|
|
672
|
+
display: flex;
|
|
673
|
+
gap: 0;
|
|
674
|
+
border-bottom: 1px solid var(--border);
|
|
675
|
+
margin-bottom: 1.5rem;
|
|
676
|
+
overflow-x: auto;
|
|
677
|
+
}
|
|
678
|
+
.tab-btn {
|
|
679
|
+
padding: 0.6rem 1rem;
|
|
680
|
+
background: none;
|
|
681
|
+
border: none;
|
|
682
|
+
border-bottom: 2px solid transparent;
|
|
683
|
+
color: var(--text-muted);
|
|
684
|
+
font-size: 0.8rem;
|
|
685
|
+
font-family: inherit;
|
|
686
|
+
cursor: pointer;
|
|
687
|
+
white-space: nowrap;
|
|
688
|
+
transition: all var(--transition-fast);
|
|
689
|
+
}
|
|
690
|
+
.tab-btn:hover { color: var(--text-bright); background: var(--bg-hover); }
|
|
691
|
+
.tab-btn.active { color: var(--primary-light); border-bottom-color: var(--primary); font-weight: 500; }
|
|
692
|
+
.tab-panel { display: none; }
|
|
693
|
+
.tab-panel.active { display: block; }
|
|
694
|
+
|
|
695
|
+
/* ============================================
|
|
696
|
+
ENTITY TABLE
|
|
697
|
+
============================================ */
|
|
698
|
+
.entity-block {
|
|
699
|
+
background: var(--bg-card);
|
|
700
|
+
border: 1px solid var(--border);
|
|
701
|
+
border-radius: 10px;
|
|
702
|
+
margin-bottom: 1rem;
|
|
703
|
+
overflow: hidden;
|
|
704
|
+
}
|
|
705
|
+
.entity-header {
|
|
706
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
707
|
+
padding: 0.75rem 1rem;
|
|
708
|
+
background: var(--bg-hover);
|
|
709
|
+
border-bottom: 1px solid var(--border);
|
|
710
|
+
}
|
|
711
|
+
.entity-name { font-weight: 600; color: var(--text-bright); }
|
|
712
|
+
.entity-desc { font-size: 0.8rem; color: var(--text-muted); }
|
|
713
|
+
.attr-table { width: 100%; border-collapse: collapse; }
|
|
714
|
+
.attr-table th {
|
|
715
|
+
text-align: left; font-size: 0.7rem; text-transform: uppercase;
|
|
716
|
+
letter-spacing: 0.05em; color: var(--text-muted); padding: 0.5rem 0.75rem;
|
|
717
|
+
border-bottom: 1px solid var(--border); font-weight: 600;
|
|
718
|
+
}
|
|
719
|
+
.attr-table td {
|
|
720
|
+
padding: 0.5rem 0.75rem; font-size: 0.85rem;
|
|
721
|
+
border-bottom: 1px solid rgba(51,65,85,0.3);
|
|
722
|
+
}
|
|
723
|
+
.attr-required { color: var(--error); font-weight: 700; }
|
|
724
|
+
|
|
725
|
+
/* ============================================
|
|
726
|
+
DEPENDENCY VISUALIZATION
|
|
727
|
+
============================================ */
|
|
728
|
+
.dep-graph {
|
|
729
|
+
display: flex; flex-direction: column; gap: 1.5rem;
|
|
730
|
+
padding: 1.5rem; background: var(--bg-card); border: 1px solid var(--border);
|
|
731
|
+
border-radius: 10px;
|
|
732
|
+
}
|
|
733
|
+
.dep-layer {
|
|
734
|
+
display: flex; align-items: center; gap: 1rem;
|
|
735
|
+
}
|
|
736
|
+
.dep-layer-label {
|
|
737
|
+
font-size: 0.7rem; color: var(--text-muted); text-transform: uppercase;
|
|
738
|
+
letter-spacing: 0.05em; min-width: 80px; font-weight: 600;
|
|
739
|
+
}
|
|
740
|
+
.dep-layer-modules { display: flex; gap: 0.75rem; flex-wrap: wrap; }
|
|
741
|
+
.dep-module {
|
|
742
|
+
padding: 0.4rem 0.8rem; border-radius: 6px;
|
|
743
|
+
background: rgba(99,102,241,0.1); border: 1px solid rgba(99,102,241,0.3);
|
|
744
|
+
color: var(--primary-light); font-size: 0.8rem; font-weight: 500;
|
|
745
|
+
}
|
|
746
|
+
.dep-arrow {
|
|
747
|
+
text-align: center; color: var(--text-muted); font-size: 1.2rem;
|
|
748
|
+
padding-left: 80px;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/* ============================================
|
|
752
|
+
STAT CARDS (Handoff)
|
|
753
|
+
============================================ */
|
|
754
|
+
.stat-grid {
|
|
755
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
756
|
+
gap: 1rem; margin-bottom: 1.5rem;
|
|
757
|
+
}
|
|
758
|
+
.stat-card {
|
|
759
|
+
background: var(--bg-card); border: 1px solid var(--border);
|
|
760
|
+
border-radius: 10px; padding: 1rem; text-align: center;
|
|
761
|
+
}
|
|
762
|
+
.stat-value { font-size: 1.8rem; font-weight: 700; color: var(--text-bright); }
|
|
763
|
+
.stat-label { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.2rem; }
|
|
764
|
+
|
|
765
|
+
/* ============================================
|
|
766
|
+
CONSOLIDATION
|
|
767
|
+
============================================ */
|
|
768
|
+
.interaction-item {
|
|
769
|
+
display: flex; align-items: center; gap: 0.75rem;
|
|
770
|
+
padding: 0.75rem 1rem; background: var(--bg-card);
|
|
771
|
+
border: 1px solid var(--border); border-radius: 8px;
|
|
772
|
+
margin-bottom: 0.5rem; font-size: 0.85rem;
|
|
773
|
+
}
|
|
774
|
+
.interaction-arrow { color: var(--primary-light); font-weight: 700; font-size: 1.1rem; }
|
|
775
|
+
.interaction-type {
|
|
776
|
+
font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.05em;
|
|
777
|
+
padding: 0.15rem 0.4rem; border-radius: 4px;
|
|
778
|
+
background: rgba(6,182,212,0.1); color: var(--accent);
|
|
779
|
+
}
|
|
780
|
+
.e2e-flow {
|
|
781
|
+
display: flex; align-items: center; gap: 0.3rem;
|
|
782
|
+
overflow-x: auto; padding: 1rem 0;
|
|
783
|
+
}
|
|
784
|
+
.e2e-step {
|
|
785
|
+
padding: 0.5rem 0.75rem; border-radius: 6px; text-align: center;
|
|
786
|
+
font-size: 0.75rem; flex-shrink: 0;
|
|
787
|
+
background: var(--bg-card); border: 1px solid var(--border);
|
|
788
|
+
}
|
|
789
|
+
.e2e-step-module { font-weight: 600; color: var(--primary-light); font-size: 0.65rem; }
|
|
790
|
+
.e2e-step-action { color: var(--text-bright); }
|
|
791
|
+
|
|
792
|
+
/* ============================================
|
|
793
|
+
DATA MODEL (Consolidation)
|
|
794
|
+
============================================ */
|
|
795
|
+
.dm-summary {
|
|
796
|
+
display: flex; gap: 1.5rem; margin-bottom: 1.5rem;
|
|
797
|
+
padding: 1rem 1.5rem; background: var(--bg-card);
|
|
798
|
+
border: 1px solid var(--border); border-radius: 10px;
|
|
799
|
+
}
|
|
800
|
+
.dm-summary-item { display: flex; align-items: baseline; gap: 0.4rem; }
|
|
801
|
+
.dm-summary-value { font-size: 1.4rem; font-weight: 700; color: var(--text-bright); }
|
|
802
|
+
.dm-summary-label { font-size: 0.8rem; color: var(--text-muted); }
|
|
803
|
+
.dm-module-group { margin-bottom: 1.5rem; }
|
|
804
|
+
.dm-module-header {
|
|
805
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
806
|
+
padding: 0.5rem 0; margin-bottom: 0.75rem;
|
|
807
|
+
border-bottom: 2px solid var(--primary);
|
|
808
|
+
}
|
|
809
|
+
.dm-module-name { font-weight: 700; color: var(--primary-light); font-size: 1rem; }
|
|
810
|
+
.dm-module-count { font-size: 0.75rem; color: var(--text-muted); }
|
|
811
|
+
.dm-entity-grid {
|
|
812
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 1rem;
|
|
813
|
+
}
|
|
814
|
+
.dm-entity-card {
|
|
815
|
+
background: var(--bg-card); border: 1px solid var(--border);
|
|
816
|
+
border-radius: 10px; overflow: hidden;
|
|
817
|
+
transition: border-color var(--transition-fast);
|
|
818
|
+
}
|
|
819
|
+
.dm-entity-card:hover { border-color: var(--border-light); }
|
|
820
|
+
.dm-entity-header {
|
|
821
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
822
|
+
padding: 0.6rem 0.75rem; background: var(--bg-hover);
|
|
823
|
+
border-bottom: 1px solid var(--border);
|
|
824
|
+
}
|
|
825
|
+
.dm-entity-name { font-weight: 600; color: var(--text-bright); font-size: 0.95rem; }
|
|
826
|
+
.dm-entity-attr-count {
|
|
827
|
+
font-size: 0.65rem; color: var(--text-muted);
|
|
828
|
+
background: rgba(99,102,241,0.1); padding: 0.1rem 0.4rem; border-radius: 4px;
|
|
829
|
+
}
|
|
830
|
+
.dm-entity-desc {
|
|
831
|
+
padding: 0.5rem 0.75rem; font-size: 0.8rem; color: var(--text-muted);
|
|
832
|
+
border-bottom: 1px solid var(--border);
|
|
833
|
+
}
|
|
834
|
+
.dm-attr-table { width: 100%; border-collapse: collapse; }
|
|
835
|
+
.dm-attr-table th {
|
|
836
|
+
text-align: left; font-size: 0.65rem; text-transform: uppercase;
|
|
837
|
+
letter-spacing: 0.05em; color: var(--text-muted); padding: 0.4rem 0.75rem;
|
|
838
|
+
border-bottom: 1px solid var(--border); font-weight: 600;
|
|
839
|
+
}
|
|
840
|
+
.dm-attr-table td { padding: 0.35rem 0.75rem; font-size: 0.8rem; border-bottom: 1px solid rgba(51,65,85,0.2); }
|
|
841
|
+
.dm-attr-name { font-weight: 500; color: var(--text-bright); white-space: nowrap; }
|
|
842
|
+
.dm-attr-desc { color: var(--text-muted); }
|
|
843
|
+
.dm-relations {
|
|
844
|
+
padding: 0.5rem 0.75rem; border-top: 1px solid var(--border);
|
|
845
|
+
background: rgba(6,182,212,0.03);
|
|
846
|
+
}
|
|
847
|
+
.dm-relations-title {
|
|
848
|
+
font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.05em;
|
|
849
|
+
color: var(--accent); font-weight: 600; margin-bottom: 0.3rem;
|
|
850
|
+
}
|
|
851
|
+
.dm-relation-item {
|
|
852
|
+
font-size: 0.8rem; color: var(--text); padding: 0.15rem 0;
|
|
853
|
+
padding-left: 0.75rem; border-left: 2px solid var(--accent);
|
|
854
|
+
margin-bottom: 0.2rem;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* ============================================
|
|
858
|
+
STRUCTURE TAB (Sections/Resources)
|
|
859
|
+
============================================ */
|
|
860
|
+
.struct-section {
|
|
861
|
+
background: var(--bg-card);
|
|
862
|
+
border: 1px solid var(--border);
|
|
863
|
+
border-radius: 10px;
|
|
864
|
+
margin-bottom: 1rem;
|
|
865
|
+
overflow: hidden;
|
|
866
|
+
}
|
|
867
|
+
.struct-section-header {
|
|
868
|
+
display: flex;
|
|
869
|
+
align-items: center;
|
|
870
|
+
gap: 0.75rem;
|
|
871
|
+
padding: 0.75rem 1rem;
|
|
872
|
+
background: var(--bg-hover);
|
|
873
|
+
border-bottom: 1px solid var(--border);
|
|
874
|
+
}
|
|
875
|
+
.struct-section-code {
|
|
876
|
+
font-weight: 600;
|
|
877
|
+
color: var(--text-bright);
|
|
878
|
+
font-size: 0.95rem;
|
|
879
|
+
}
|
|
880
|
+
.struct-section-desc {
|
|
881
|
+
flex: 1;
|
|
882
|
+
font-size: 0.8rem;
|
|
883
|
+
color: var(--text-muted);
|
|
884
|
+
}
|
|
885
|
+
.struct-section-badge {
|
|
886
|
+
font-size: 0.65rem;
|
|
887
|
+
color: var(--text-muted);
|
|
888
|
+
background: rgba(99,102,241,0.1);
|
|
889
|
+
padding: 0.1rem 0.5rem;
|
|
890
|
+
border-radius: 4px;
|
|
891
|
+
white-space: nowrap;
|
|
892
|
+
}
|
|
893
|
+
.struct-resources {
|
|
894
|
+
padding: 0.5rem 0;
|
|
895
|
+
}
|
|
896
|
+
.struct-resource {
|
|
897
|
+
display: flex;
|
|
898
|
+
align-items: baseline;
|
|
899
|
+
gap: 0.5rem;
|
|
900
|
+
padding: 0.35rem 1rem 0.35rem 1.5rem;
|
|
901
|
+
font-size: 0.85rem;
|
|
902
|
+
transition: background var(--transition-fast);
|
|
903
|
+
}
|
|
904
|
+
.struct-resource:hover {
|
|
905
|
+
background: var(--bg-hover);
|
|
906
|
+
}
|
|
907
|
+
.struct-resource-icon {
|
|
908
|
+
color: var(--primary-light);
|
|
909
|
+
font-size: 0.7rem;
|
|
910
|
+
flex-shrink: 0;
|
|
911
|
+
}
|
|
912
|
+
.struct-resource-name {
|
|
913
|
+
font-weight: 500;
|
|
914
|
+
color: var(--text-bright);
|
|
915
|
+
}
|
|
916
|
+
.struct-resource-desc {
|
|
917
|
+
font-size: 0.8rem;
|
|
918
|
+
color: var(--text-muted);
|
|
919
|
+
}
|
|
930
920
|
|
|
931
921
|
|
|
932
922
|
/* --- 06-wireframes.css --- */
|
|
@@ -1959,55 +1949,6 @@ body {
|
|
|
1959
1949
|
</div>
|
|
1960
1950
|
</div>
|
|
1961
1951
|
|
|
1962
|
-
<!-- SECTION: Risques et hypotheses -->
|
|
1963
|
-
<div class="section" id="cadrage-risks" style="display:none;" data-vibe-hide>
|
|
1964
|
-
<h2 class="section-title">Risques et hypotheses</h2>
|
|
1965
|
-
<p class="section-subtitle">Ce qui pourrait mal tourner et les certitudes non verifiees.</p>
|
|
1966
|
-
|
|
1967
|
-
<h3 style="color: var(--text-bright); font-size: 1rem; margin-bottom: 0.75rem;">Risques identifies</h3>
|
|
1968
|
-
<div id="risksList"></div>
|
|
1969
|
-
<button class="add-btn" onclick="toggleForm('addRiskForm')">+ Ajouter un risque</button>
|
|
1970
|
-
|
|
1971
|
-
<div class="inline-form" id="addRiskForm">
|
|
1972
|
-
<div class="inline-form-title">Nouveau risque</div>
|
|
1973
|
-
<div class="form-group">
|
|
1974
|
-
<label class="form-label">Description du risque</label>
|
|
1975
|
-
<input type="text" class="form-input" id="risk-desc" placeholder="Qu'est-ce qui pourrait mal tourner ?">
|
|
1976
|
-
</div>
|
|
1977
|
-
<div class="form-row">
|
|
1978
|
-
<div class="form-group">
|
|
1979
|
-
<label class="form-label">Probabilite</label>
|
|
1980
|
-
<select class="form-select" id="risk-probability">
|
|
1981
|
-
<option value="high">Forte</option>
|
|
1982
|
-
<option value="medium">Moyenne</option>
|
|
1983
|
-
<option value="low">Faible</option>
|
|
1984
|
-
</select>
|
|
1985
|
-
</div>
|
|
1986
|
-
<div class="form-group">
|
|
1987
|
-
<label class="form-label">Impact</label>
|
|
1988
|
-
<select class="form-select" id="risk-impact">
|
|
1989
|
-
<option value="high">Grave</option>
|
|
1990
|
-
<option value="medium">Moyen</option>
|
|
1991
|
-
<option value="low">Faible</option>
|
|
1992
|
-
</select>
|
|
1993
|
-
</div>
|
|
1994
|
-
</div>
|
|
1995
|
-
<div class="form-group">
|
|
1996
|
-
<label class="form-label">Mesure de prevention ou de reduction</label>
|
|
1997
|
-
<textarea class="form-textarea" id="risk-mitigation" placeholder="Comment prevenir ou reduire ce risque ?"></textarea>
|
|
1998
|
-
</div>
|
|
1999
|
-
<div class="form-actions">
|
|
2000
|
-
<button class="btn" onclick="toggleForm('addRiskForm')">Annuler</button>
|
|
2001
|
-
<button class="btn btn-primary" onclick="addRisk()">Ajouter ce risque</button>
|
|
2002
|
-
</div>
|
|
2003
|
-
</div>
|
|
2004
|
-
|
|
2005
|
-
<h3 style="color: var(--text-bright); font-size: 1rem; margin: 2rem 0 0.75rem;">Hypotheses a verifier</h3>
|
|
2006
|
-
<div class="card">
|
|
2007
|
-
<div class="editable" contenteditable="true" data-field="risks.assumptions" data-placeholder="Quelles hypotheses faites-vous sur ce projet sans les avoir verifiees ? (une par ligne)"></div>
|
|
2008
|
-
</div>
|
|
2009
|
-
</div>
|
|
2010
|
-
|
|
2011
1952
|
<!-- SECTION: Criteres de reussite -->
|
|
2012
1953
|
<div class="section" id="cadrage-success" style="display:none;">
|
|
2013
1954
|
<h2 class="section-title">Criteres de reussite</h2>
|
|
@@ -2295,7 +2236,6 @@ data.specComments = data.specComments || {};
|
|
|
2295
2236
|
data.customRoles = data.customRoles || [];
|
|
2296
2237
|
data.customActions = data.customActions || [];
|
|
2297
2238
|
data.cadrage.criteria = data.cadrage.criteria || [];
|
|
2298
|
-
data.cadrage.risks = data.cadrage.risks || [];
|
|
2299
2239
|
data.consolidation = data.consolidation || {};
|
|
2300
2240
|
data.consolidation.e2eFlows = data.consolidation.e2eFlows || [];
|
|
2301
2241
|
|
|
@@ -2431,7 +2371,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
2431
2371
|
}
|
|
2432
2372
|
renderStakeholders();
|
|
2433
2373
|
renderScope();
|
|
2434
|
-
renderRisks();
|
|
2435
2374
|
renderCriteria();
|
|
2436
2375
|
renderModules();
|
|
2437
2376
|
renderDependencies();
|
|
@@ -2443,6 +2382,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
2443
2382
|
updateDepSelects();
|
|
2444
2383
|
initInlineComments();
|
|
2445
2384
|
renderReviewPanel();
|
|
2385
|
+
showSection(currentSectionId);
|
|
2446
2386
|
});
|
|
2447
2387
|
|
|
2448
2388
|
|
|
@@ -2562,7 +2502,6 @@ function buildCadrageItems() {
|
|
|
2562
2502
|
return renderNavItem('cadrage-context', 'Contexte') +
|
|
2563
2503
|
renderNavItem('cadrage-stakeholders', 'Parties prenantes', data.cadrage.stakeholders.length) +
|
|
2564
2504
|
renderNavItem('cadrage-scope', 'Perimetre fonctionnel') +
|
|
2565
|
-
(isVibeCoding ? '' : renderNavItem('cadrage-risks', 'Risques et hypotheses', data.cadrage.risks.length)) +
|
|
2566
2505
|
renderNavItem('cadrage-success', 'Criteres de reussite');
|
|
2567
2506
|
}
|
|
2568
2507
|
|
|
@@ -2839,53 +2778,14 @@ function toggleCriterion(index) {
|
|
|
2839
2778
|
function renderCriteria() {
|
|
2840
2779
|
const container = document.getElementById('criteriaList');
|
|
2841
2780
|
if (!container) return;
|
|
2842
|
-
const criteria = data.cadrage.criteria || [];
|
|
2843
|
-
container.innerHTML = criteria.map((c, i) => `
|
|
2844
|
-
<div class="uc-item" style="display:flex;align-items:center;gap:0.75rem;">
|
|
2845
|
-
<input type="checkbox" ${c.validated ? 'checked' : ''} onchange="toggleCriterion(${i})" style="cursor:pointer;width:18px;height:18px;flex-shrink:0;">
|
|
2846
|
-
<span style="flex:1;${c.validated ? 'text-decoration:line-through;color:var(--text-muted);' : ''}">${c.text}</span>
|
|
2847
|
-
<button class="btn btn-sm" onclick="removeCriterion(${i})" style="opacity:0.5;flex-shrink:0;">✕</button>
|
|
2848
|
-
</div>
|
|
2849
|
-
`).join('');
|
|
2850
|
-
}
|
|
2851
|
-
|
|
2852
|
-
/* ============================================
|
|
2853
|
-
RISKS
|
|
2854
|
-
============================================ */
|
|
2855
|
-
function addRisk() {
|
|
2856
|
-
const desc = document.getElementById('risk-desc').value.trim();
|
|
2857
|
-
if (!desc) return;
|
|
2858
|
-
|
|
2859
|
-
data.cadrage.risks.push({
|
|
2860
|
-
description: desc,
|
|
2861
|
-
probability: document.getElementById('risk-probability').value,
|
|
2862
|
-
impact: document.getElementById('risk-impact').value,
|
|
2863
|
-
mitigation: document.getElementById('risk-mitigation').value.trim()
|
|
2864
|
-
});
|
|
2865
|
-
|
|
2866
|
-
renderRisks();
|
|
2867
|
-
toggleForm('addRiskForm');
|
|
2868
|
-
clearForm('addRiskForm');
|
|
2869
|
-
autoSave();
|
|
2870
|
-
}
|
|
2871
|
-
|
|
2872
|
-
function renderRisks() {
|
|
2873
|
-
const list = document.getElementById('risksList');
|
|
2874
|
-
list.innerHTML = data.cadrage.risks.map((r, i) => {
|
|
2875
|
-
const level = (r.probability === 'high' && r.impact === 'high') ? 'critical'
|
|
2876
|
-
: (r.probability === 'low' && r.impact === 'low') ? 'low' : 'medium';
|
|
2877
|
-
return `
|
|
2878
|
-
<div class="risk-item">
|
|
2879
|
-
<div class="risk-level risk-${level}"></div>
|
|
2880
|
-
<div>
|
|
2881
|
-
<div class="risk-text">${r.description}</div>
|
|
2882
|
-
${r.mitigation ? '<div style="font-size:0.75rem;color:var(--text-muted);margin-top:0.25rem;">Prevention : ' + r.mitigation + '</div>' : ''}
|
|
2883
|
-
</div>
|
|
2884
|
-
<div class="risk-probability">${formatLevel(r.probability)}</div>
|
|
2885
|
-
<div class="risk-impact">${formatLevel(r.impact)}</div>
|
|
2886
|
-
</div>
|
|
2887
|
-
`;
|
|
2888
|
-
}).join('');
|
|
2781
|
+
const criteria = data.cadrage.criteria || [];
|
|
2782
|
+
container.innerHTML = criteria.map((c, i) => `
|
|
2783
|
+
<div class="uc-item" style="display:flex;align-items:center;gap:0.75rem;">
|
|
2784
|
+
<input type="checkbox" ${c.validated ? 'checked' : ''} onchange="toggleCriterion(${i})" style="cursor:pointer;width:18px;height:18px;flex-shrink:0;">
|
|
2785
|
+
<span style="flex:1;${c.validated ? 'text-decoration:line-through;color:var(--text-muted);' : ''}">${c.text}</span>
|
|
2786
|
+
<button class="btn btn-sm" onclick="removeCriterion(${i})" style="opacity:0.5;flex-shrink:0;">✕</button>
|
|
2787
|
+
</div>
|
|
2788
|
+
`).join('');
|
|
2889
2789
|
}
|
|
2890
2790
|
|
|
2891
2791
|
/* ============================================
|
|
@@ -3602,7 +3502,6 @@ function renderWireframeMockup(code, wf, i) {
|
|
|
3602
3502
|
</div>
|
|
3603
3503
|
`;
|
|
3604
3504
|
}
|
|
3605
|
-
}
|
|
3606
3505
|
|
|
3607
3506
|
function toggleWireframeView(wireframeId, view) {
|
|
3608
3507
|
const container = document.getElementById(wireframeId);
|
|
@@ -4399,7 +4298,6 @@ function renderHandoffStats() {
|
|
|
4399
4298
|
<div class="stat-card"><div class="stat-value">${totalStakeholders}</div><div class="stat-label">Profils utilisateurs</div></div>
|
|
4400
4299
|
<div class="stat-card"><div class="stat-value">${data.dependencies.length}</div><div class="stat-label">Dependances</div></div>
|
|
4401
4300
|
<div class="stat-card"><div class="stat-value">${data.consolidation.e2eFlows.length}</div><div class="stat-label">Parcours bout en bout</div></div>
|
|
4402
|
-
${isVibeCoding ? '' : `<div class="stat-card"><div class="stat-value">${data.cadrage.risks.length}</div><div class="stat-label">Risques identifies</div></div>`}
|
|
4403
4301
|
`;
|
|
4404
4302
|
}
|
|
4405
4303
|
|
|
@@ -4468,141 +4366,139 @@ function renderCoverageMatrix() {
|
|
|
4468
4366
|
|
|
4469
4367
|
|
|
4470
4368
|
/* --- 08-editing.js --- */
|
|
4471
|
-
/* ============================================
|
|
4472
|
-
PERSISTENCE
|
|
4473
|
-
============================================ */
|
|
4474
|
-
function autoSave() {
|
|
4475
|
-
data.metadata.lastModified = new Date().toISOString();
|
|
4476
|
-
localStorage.setItem(APP_KEY, JSON.stringify(data));
|
|
4477
|
-
}
|
|
4478
|
-
|
|
4479
|
-
function saveToLocalStorage() {
|
|
4480
|
-
autoSave();
|
|
4481
|
-
showNotification('Modifications sauvegardees');
|
|
4482
|
-
}
|
|
4483
|
-
|
|
4484
|
-
function resetToEmbedded() {
|
|
4485
|
-
if (!confirm('Reinitialiser toutes les donnees depuis la version d\'origine ? Vos modifications locales (commentaires, notes) seront perdues.')) return;
|
|
4486
|
-
localStorage.removeItem(APP_KEY);
|
|
4487
|
-
// Restore data from ORIGINAL_DATA
|
|
4488
|
-
Object.keys(data).forEach(k => delete data[k]);
|
|
4489
|
-
Object.assign(data, JSON.parse(JSON.stringify(ORIGINAL_DATA)));
|
|
4490
|
-
// Re-render everything
|
|
4491
|
-
initEditableFields();
|
|
4492
|
-
renderStakeholders();
|
|
4493
|
-
renderScope();
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
+ '|' +
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
const
|
|
4521
|
-
const
|
|
4522
|
-
const
|
|
4523
|
-
const
|
|
4524
|
-
const
|
|
4525
|
-
const
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
//
|
|
4530
|
-
|
|
4531
|
-
data.
|
|
4532
|
-
data.
|
|
4533
|
-
data.
|
|
4534
|
-
data.
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
//
|
|
4549
|
-
//
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
}
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
// Preserve user
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
}
|
|
4587
|
-
|
|
4588
|
-
data.
|
|
4589
|
-
data.
|
|
4590
|
-
data.
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
} catch (e) { console.error('Error loading saved data:', e); }
|
|
4605
|
-
}
|
|
4369
|
+
/* ============================================
|
|
4370
|
+
PERSISTENCE
|
|
4371
|
+
============================================ */
|
|
4372
|
+
function autoSave() {
|
|
4373
|
+
data.metadata.lastModified = new Date().toISOString();
|
|
4374
|
+
localStorage.setItem(APP_KEY, JSON.stringify(data));
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
function saveToLocalStorage() {
|
|
4378
|
+
autoSave();
|
|
4379
|
+
showNotification('Modifications sauvegardees');
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
function resetToEmbedded() {
|
|
4383
|
+
if (!confirm('Reinitialiser toutes les donnees depuis la version d\'origine ? Vos modifications locales (commentaires, notes) seront perdues.')) return;
|
|
4384
|
+
localStorage.removeItem(APP_KEY);
|
|
4385
|
+
// Restore data from ORIGINAL_DATA
|
|
4386
|
+
Object.keys(data).forEach(k => delete data[k]);
|
|
4387
|
+
Object.assign(data, JSON.parse(JSON.stringify(ORIGINAL_DATA)));
|
|
4388
|
+
// Re-render everything
|
|
4389
|
+
initEditableFields();
|
|
4390
|
+
renderStakeholders();
|
|
4391
|
+
renderScope();
|
|
4392
|
+
renderCriteria();
|
|
4393
|
+
renderModules();
|
|
4394
|
+
renderDependencies();
|
|
4395
|
+
renderAllModuleSpecs();
|
|
4396
|
+
renderConsolidation();
|
|
4397
|
+
renderHandoff();
|
|
4398
|
+
renderE2EFlows();
|
|
4399
|
+
updateCounts();
|
|
4400
|
+
renderReviewPanel();
|
|
4401
|
+
showNotification('Donnees reinitialisees depuis la version d\'origine');
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
function loadFromLocalStorage() {
|
|
4405
|
+
const saved = localStorage.getItem(APP_KEY);
|
|
4406
|
+
if (!saved) return;
|
|
4407
|
+
|
|
4408
|
+
try {
|
|
4409
|
+
const parsed = JSON.parse(saved);
|
|
4410
|
+
|
|
4411
|
+
// Build fingerprint of embedded structural data to detect HTML regeneration
|
|
4412
|
+
const embeddedFingerprint = ORIGINAL_DATA.modules.map(m => m.code).sort().join(',')
|
|
4413
|
+
+ '|' + (ORIGINAL_DATA.metadata?.createdAt || '')
|
|
4414
|
+
+ '|' + ORIGINAL_DATA.modules.length;
|
|
4415
|
+
const cachedFingerprint = parsed._structureFingerprint || '';
|
|
4416
|
+
|
|
4417
|
+
const structureChanged = embeddedFingerprint !== cachedFingerprint;
|
|
4418
|
+
const embeddedModuleCount = ORIGINAL_DATA.modules?.length || 0;
|
|
4419
|
+
const cachedModuleCount = (parsed.modules || []).length;
|
|
4420
|
+
const hasNewModules = embeddedModuleCount > cachedModuleCount;
|
|
4421
|
+
const embeddedModuleCodes = new Set(ORIGINAL_DATA.modules.map(m => m.code));
|
|
4422
|
+
const cachedModuleCodes = new Set((parsed.modules || []).map(m => m.code));
|
|
4423
|
+
const missingModules = [...embeddedModuleCodes].filter(c => !cachedModuleCodes.has(c));
|
|
4424
|
+
|
|
4425
|
+
if (structureChanged || hasNewModules || missingModules.length > 0) {
|
|
4426
|
+
// HTML was regenerated or has new modules — keep embedded structural data
|
|
4427
|
+
// Only restore user-specific edits (comments, custom roles, notes)
|
|
4428
|
+
data.wireframeComments = parsed.wireframeComments || {};
|
|
4429
|
+
data.specComments = parsed.specComments || {};
|
|
4430
|
+
data.customRoles = parsed.customRoles || [];
|
|
4431
|
+
data.customActions = parsed.customActions || [];
|
|
4432
|
+
data.comments = parsed.comments || [];
|
|
4433
|
+
|
|
4434
|
+
// Merge user-added notes per module (preserve existing module notes)
|
|
4435
|
+
for (const code of Object.keys(parsed.moduleSpecs || {})) {
|
|
4436
|
+
if (data.moduleSpecs[code] && parsed.moduleSpecs[code]?.notes) {
|
|
4437
|
+
data.moduleSpecs[code].notes = parsed.moduleSpecs[code].notes;
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
|
|
4441
|
+
// Save fresh embedded data with fingerprint
|
|
4442
|
+
data._structureFingerprint = embeddedFingerprint;
|
|
4443
|
+
autoSave();
|
|
4444
|
+
} else {
|
|
4445
|
+
// Cache matches current structure — safe to restore user edits on cadrage/notes
|
|
4446
|
+
// IMPORTANT: Always keep embedded modules and moduleSpecs as structural source of truth
|
|
4447
|
+
// Only merge cadrage user-editable fields and notes
|
|
4448
|
+
if (parsed.cadrage) {
|
|
4449
|
+
// Merge cadrage context (user may have edited text fields)
|
|
4450
|
+
if (parsed.cadrage.context) {
|
|
4451
|
+
data.cadrage.context = { ...data.cadrage.context, ...parsed.cadrage.context };
|
|
4452
|
+
}
|
|
4453
|
+
// Merge scope only if user added items interactively
|
|
4454
|
+
if (parsed.cadrage.scope) {
|
|
4455
|
+
data.cadrage.scope = { ...data.cadrage.scope, ...parsed.cadrage.scope };
|
|
4456
|
+
}
|
|
4457
|
+
// Merge stakeholders if user edited them
|
|
4458
|
+
if (parsed.cadrage.stakeholders) data.cadrage.stakeholders = parsed.cadrage.stakeholders;
|
|
4459
|
+
}
|
|
4460
|
+
data.dependencies = parsed.dependencies || data.dependencies;
|
|
4461
|
+
data.consolidation = { ...data.consolidation, ...(parsed.consolidation || {}) };
|
|
4462
|
+
|
|
4463
|
+
// Merge moduleSpecs: keep embedded structure, overlay user-editable fields (notes, custom permissions)
|
|
4464
|
+
for (const code of Object.keys(data.moduleSpecs)) {
|
|
4465
|
+
const cached = parsed.moduleSpecs?.[code];
|
|
4466
|
+
if (cached) {
|
|
4467
|
+
// Preserve user notes
|
|
4468
|
+
if (cached.notes) data.moduleSpecs[code].notes = cached.notes;
|
|
4469
|
+
// Preserve user-added use cases/rules/entities (merged with embedded)
|
|
4470
|
+
// Only add items that don't exist in embedded (by name match)
|
|
4471
|
+
const embeddedUcNames = new Set((data.moduleSpecs[code].useCases || []).map(uc => uc.name));
|
|
4472
|
+
(cached.useCases || []).forEach(uc => {
|
|
4473
|
+
if (!embeddedUcNames.has(uc.name)) data.moduleSpecs[code].useCases.push(uc);
|
|
4474
|
+
});
|
|
4475
|
+
const embeddedBrNames = new Set((data.moduleSpecs[code].businessRules || []).map(br => br.name));
|
|
4476
|
+
(cached.businessRules || []).forEach(br => {
|
|
4477
|
+
if (!embeddedBrNames.has(br.name)) data.moduleSpecs[code].businessRules.push(br);
|
|
4478
|
+
});
|
|
4479
|
+
// Preserve user permissions edits
|
|
4480
|
+
if (cached.permissions) data.moduleSpecs[code].permissions = cached.permissions;
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
data.wireframeComments = parsed.wireframeComments || {};
|
|
4485
|
+
data.specComments = parsed.specComments || {};
|
|
4486
|
+
data.customRoles = parsed.customRoles || [];
|
|
4487
|
+
data.customActions = parsed.customActions || [];
|
|
4488
|
+
data.comments = parsed.comments || [];
|
|
4489
|
+
|
|
4490
|
+
// Update fingerprint
|
|
4491
|
+
data._structureFingerprint = embeddedFingerprint;
|
|
4492
|
+
autoSave();
|
|
4493
|
+
}
|
|
4494
|
+
|
|
4495
|
+
// Restore editable fields
|
|
4496
|
+
document.querySelectorAll('.editable[data-field]').forEach(el => {
|
|
4497
|
+
const value = getNestedValue(data, 'cadrage.' + el.dataset.field);
|
|
4498
|
+
if (value) el.textContent = value;
|
|
4499
|
+
});
|
|
4500
|
+
} catch (e) { console.error('Error loading saved data:', e); }
|
|
4501
|
+
}
|
|
4606
4502
|
|
|
4607
4503
|
|
|
4608
4504
|
/* --- 09-export.js --- */
|
|
@@ -4777,374 +4673,373 @@ function saveReviewJSON() {
|
|
|
4777
4673
|
|
|
4778
4674
|
|
|
4779
4675
|
/* --- 10-comments.js --- */
|
|
4780
|
-
/* ============================================
|
|
4781
|
-
INLINE COMMENTS SYSTEM
|
|
4782
|
-
============================================ */
|
|
4783
|
-
|
|
4784
|
-
/**
|
|
4785
|
-
* Comments are stored in data.comments[] with structure:
|
|
4786
|
-
* { id, sectionId, cardIndex, author, timestamp, content, status, category }
|
|
4787
|
-
*
|
|
4788
|
-
* - sectionId: matches the section div id (e.g. "cadrage-problem", "module-spec-Clients")
|
|
4789
|
-
* - cardIndex: index of the card within that section (0-based)
|
|
4790
|
-
* - status: "to-review" | "validated"
|
|
4791
|
-
* - category: "clarification" | "correction" | "suggestion"
|
|
4792
|
-
*/
|
|
4793
|
-
|
|
4794
|
-
function initInlineComments() {
|
|
4795
|
-
// Cadrage sections: direct card children
|
|
4796
|
-
document.querySelectorAll('.section > .card, .section > .stakeholder-card, .section > .uc-item
|
|
4797
|
-
if (card.dataset.commentInitialized) return;
|
|
4798
|
-
card.dataset.commentInitialized = 'true';
|
|
4799
|
-
var section = card.closest('.section');
|
|
4800
|
-
var sectionId = section ? section.id : 'unknown';
|
|
4801
|
-
var siblings = Array.from(section.querySelectorAll(':scope > .card, :scope > .stakeholder-card, :scope > .uc-item
|
|
4802
|
-
var index = siblings.indexOf(card);
|
|
4803
|
-
card.appendChild(createCommentUI(sectionId, index));
|
|
4804
|
-
});
|
|
4805
|
-
|
|
4806
|
-
// Module spec lists: nested items in ucList, brList, entList containers
|
|
4807
|
-
document.querySelectorAll('[id^="ucList-"] > .uc-item, [id^="brList-"] > div, [id^="entList-"] > .entity-block').forEach(function(item) {
|
|
4808
|
-
if (item.dataset.commentInitialized) return;
|
|
4809
|
-
item.dataset.commentInitialized = 'true';
|
|
4810
|
-
var list = item.parentElement;
|
|
4811
|
-
var listId = list.id;
|
|
4812
|
-
var siblings = Array.from(list.children);
|
|
4813
|
-
var index = siblings.indexOf(item);
|
|
4814
|
-
item.appendChild(createCommentUI(listId, index));
|
|
4815
|
-
});
|
|
4816
|
-
|
|
4817
|
-
// Stakeholder cards in grid
|
|
4818
|
-
document.querySelectorAll('.stakeholder-grid > .stakeholder-card').forEach(function(card) {
|
|
4819
|
-
if (card.dataset.commentInitialized) return;
|
|
4820
|
-
card.dataset.commentInitialized = 'true';
|
|
4821
|
-
var grid = card.parentElement;
|
|
4822
|
-
var section = card.closest('.section');
|
|
4823
|
-
var sectionId = section ? section.id : 'stakeholders';
|
|
4824
|
-
var siblings = Array.from(grid.children);
|
|
4825
|
-
var index = siblings.indexOf(card);
|
|
4826
|
-
card.appendChild(createCommentUI(sectionId, index));
|
|
4827
|
-
});
|
|
4828
|
-
|
|
4829
|
-
// Scope items
|
|
4830
|
-
['scopeVital', 'scopeImportant', 'scopeOptional', 'scopeExcluded'].forEach(function(containerId) {
|
|
4831
|
-
var container = document.getElementById(containerId);
|
|
4832
|
-
if (!container) return;
|
|
4833
|
-
container.querySelectorAll('.uc-item').forEach(function(item, index) {
|
|
4834
|
-
if (item.dataset.commentInitialized) return;
|
|
4835
|
-
item.dataset.commentInitialized = 'true';
|
|
4836
|
-
item.appendChild(createCommentUI(containerId, index));
|
|
4837
|
-
});
|
|
4838
|
-
});
|
|
4839
|
-
}
|
|
4840
|
-
|
|
4841
|
-
function createCommentUI(sectionId, cardIndex) {
|
|
4842
|
-
const comments = getCommentsForCard(sectionId, cardIndex);
|
|
4843
|
-
const count = comments.length;
|
|
4844
|
-
|
|
4845
|
-
const container = document.createElement('div');
|
|
4846
|
-
container.className = 'comment-btn-container';
|
|
4847
|
-
container.dataset.sectionId = sectionId;
|
|
4848
|
-
container.dataset.cardIndex = cardIndex;
|
|
4849
|
-
|
|
4850
|
-
container.innerHTML = `
|
|
4851
|
-
<button class="comment-toggle-btn" onclick="toggleCommentThread('${sectionId}', ${cardIndex})">
|
|
4852
|
-
Commentaires <span class="comment-count ${count === 0 ? 'empty' : ''}">${count}</span>
|
|
4853
|
-
</button>
|
|
4854
|
-
<div class="comment-thread" id="comment-thread-${sectionId}-${cardIndex}">
|
|
4855
|
-
<div class="comment-items" id="comment-items-${sectionId}-${cardIndex}">
|
|
4856
|
-
${renderCommentItems(sectionId, cardIndex)}
|
|
4857
|
-
</div>
|
|
4858
|
-
<div class="comment-add-form">
|
|
4859
|
-
<textarea id="comment-text-${sectionId}-${cardIndex}" placeholder="Ajouter un commentaire..."></textarea>
|
|
4860
|
-
<select id="comment-cat-${sectionId}-${cardIndex}">
|
|
4861
|
-
<option value="clarification">Clarification</option>
|
|
4862
|
-
<option value="correction">Correction</option>
|
|
4863
|
-
<option value="suggestion">Suggestion</option>
|
|
4864
|
-
</select>
|
|
4865
|
-
<button onclick="addInlineComment('${sectionId}', ${cardIndex})">Ajouter</button>
|
|
4866
|
-
</div>
|
|
4867
|
-
</div>
|
|
4868
|
-
`;
|
|
4869
|
-
|
|
4870
|
-
return container;
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
function getCommentsForCard(sectionId, cardIndex) {
|
|
4874
|
-
return (data.comments || []).filter(c =>
|
|
4875
|
-
c.sectionId === sectionId && c.cardIndex === cardIndex
|
|
4876
|
-
);
|
|
4877
|
-
}
|
|
4878
|
-
|
|
4879
|
-
function toggleCommentThread(sectionId, cardIndex) {
|
|
4880
|
-
const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
|
|
4881
|
-
if (thread) thread.classList.toggle('visible');
|
|
4882
|
-
}
|
|
4883
|
-
|
|
4884
|
-
function renderCommentItems(sectionId, cardIndex) {
|
|
4885
|
-
const comments = getCommentsForCard(sectionId, cardIndex);
|
|
4886
|
-
if (comments.length === 0) {
|
|
4887
|
-
return '<div style="font-size:0.8rem;color:var(--text-muted);padding:0.5rem 0;font-style:italic;">Aucun commentaire</div>';
|
|
4888
|
-
}
|
|
4889
|
-
|
|
4890
|
-
return comments.map((c, i) => {
|
|
4891
|
-
const initials = (c.author || 'U').substring(0, 2).toUpperCase();
|
|
4892
|
-
const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';
|
|
4893
|
-
const globalIndex = data.comments.indexOf(c);
|
|
4894
|
-
|
|
4895
|
-
return `
|
|
4896
|
-
<div class="comment-item">
|
|
4897
|
-
<div class="comment-avatar">${initials}</div>
|
|
4898
|
-
<div class="comment-body">
|
|
4899
|
-
<div class="comment-meta">
|
|
4900
|
-
<span class="comment-author">${c.author || 'Utilisateur'}</span>
|
|
4901
|
-
<span class="comment-date">${date}</span>
|
|
4902
|
-
<span class="comment-category comment-category-${c.category}">${c.category}</span>
|
|
4903
|
-
<span class="comment-status comment-status-${c.status}">${c.status === 'validated' ? 'Valide' : 'A revoir'}</span>
|
|
4904
|
-
</div>
|
|
4905
|
-
<div class="comment-text">${c.content}</div>
|
|
4906
|
-
<div class="comment-actions">
|
|
4907
|
-
<button class="comment-action-btn" onclick="toggleCommentStatus(${globalIndex})">${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}</button>
|
|
4908
|
-
<button class="comment-action-btn" onclick="deleteComment(${globalIndex})" style="color:var(--error);">Supprimer</button>
|
|
4909
|
-
</div>
|
|
4910
|
-
</div>
|
|
4911
|
-
</div>
|
|
4912
|
-
`;
|
|
4913
|
-
}).join('');
|
|
4914
|
-
}
|
|
4915
|
-
|
|
4916
|
-
function addInlineComment(sectionId, cardIndex) {
|
|
4917
|
-
const textEl = document.getElementById('comment-text-' + sectionId + '-' + cardIndex);
|
|
4918
|
-
const catEl = document.getElementById('comment-cat-' + sectionId + '-' + cardIndex);
|
|
4919
|
-
const content = textEl.value.trim();
|
|
4920
|
-
if (!content) return;
|
|
4921
|
-
|
|
4922
|
-
const comment = {
|
|
4923
|
-
id: 'comment-' + Date.now(),
|
|
4924
|
-
sectionId: sectionId,
|
|
4925
|
-
cardIndex: cardIndex,
|
|
4926
|
-
author: 'Utilisateur',
|
|
4927
|
-
timestamp: new Date().toISOString(),
|
|
4928
|
-
content: content,
|
|
4929
|
-
status: 'to-review',
|
|
4930
|
-
category: catEl.value
|
|
4931
|
-
};
|
|
4932
|
-
|
|
4933
|
-
data.comments.push(comment);
|
|
4934
|
-
textEl.value = '';
|
|
4935
|
-
|
|
4936
|
-
refreshCommentUI(sectionId, cardIndex);
|
|
4937
|
-
renderReviewPanel();
|
|
4938
|
-
autoSave();
|
|
4939
|
-
}
|
|
4940
|
-
|
|
4941
|
-
function toggleCommentStatus(globalIndex) {
|
|
4942
|
-
const comment = data.comments[globalIndex];
|
|
4943
|
-
if (!comment) return;
|
|
4944
|
-
comment.status = comment.status === 'validated' ? 'to-review' : 'validated';
|
|
4945
|
-
refreshCommentUI(comment.sectionId, comment.cardIndex);
|
|
4946
|
-
renderReviewPanel();
|
|
4947
|
-
autoSave();
|
|
4948
|
-
}
|
|
4949
|
-
|
|
4950
|
-
function deleteComment(globalIndex) {
|
|
4951
|
-
const comment = data.comments[globalIndex];
|
|
4952
|
-
if (!comment) return;
|
|
4953
|
-
const sectionId = comment.sectionId;
|
|
4954
|
-
const cardIndex = comment.cardIndex;
|
|
4955
|
-
data.comments.splice(globalIndex, 1);
|
|
4956
|
-
refreshCommentUI(sectionId, cardIndex);
|
|
4957
|
-
renderReviewPanel();
|
|
4958
|
-
autoSave();
|
|
4959
|
-
}
|
|
4960
|
-
|
|
4961
|
-
function refreshCommentUI(sectionId, cardIndex) {
|
|
4962
|
-
// Update comment items
|
|
4963
|
-
const itemsContainer = document.getElementById('comment-items-' + sectionId + '-' + cardIndex);
|
|
4964
|
-
if (itemsContainer) {
|
|
4965
|
-
itemsContainer.innerHTML = renderCommentItems(sectionId, cardIndex);
|
|
4966
|
-
}
|
|
4967
|
-
|
|
4968
|
-
// Update count badge
|
|
4969
|
-
const count = getCommentsForCard(sectionId, cardIndex).length;
|
|
4970
|
-
const container = document.querySelector(`.comment-btn-container[data-section-id="${sectionId}"][data-card-index="${cardIndex}"]`);
|
|
4971
|
-
if (container) {
|
|
4972
|
-
const badge = container.querySelector('.comment-count');
|
|
4973
|
-
if (badge) {
|
|
4974
|
-
badge.textContent = count;
|
|
4975
|
-
badge.className = 'comment-count' + (count === 0 ? ' empty' : '');
|
|
4976
|
-
}
|
|
4977
|
-
}
|
|
4978
|
-
}
|
|
4676
|
+
/* ============================================
|
|
4677
|
+
INLINE COMMENTS SYSTEM
|
|
4678
|
+
============================================ */
|
|
4679
|
+
|
|
4680
|
+
/**
|
|
4681
|
+
* Comments are stored in data.comments[] with structure:
|
|
4682
|
+
* { id, sectionId, cardIndex, author, timestamp, content, status, category }
|
|
4683
|
+
*
|
|
4684
|
+
* - sectionId: matches the section div id (e.g. "cadrage-problem", "module-spec-Clients")
|
|
4685
|
+
* - cardIndex: index of the card within that section (0-based)
|
|
4686
|
+
* - status: "to-review" | "validated"
|
|
4687
|
+
* - category: "clarification" | "correction" | "suggestion"
|
|
4688
|
+
*/
|
|
4689
|
+
|
|
4690
|
+
function initInlineComments() {
|
|
4691
|
+
// Cadrage sections: direct card children
|
|
4692
|
+
document.querySelectorAll('.section > .card, .section > .stakeholder-card, .section > .uc-item').forEach(function(card) {
|
|
4693
|
+
if (card.dataset.commentInitialized) return;
|
|
4694
|
+
card.dataset.commentInitialized = 'true';
|
|
4695
|
+
var section = card.closest('.section');
|
|
4696
|
+
var sectionId = section ? section.id : 'unknown';
|
|
4697
|
+
var siblings = Array.from(section.querySelectorAll(':scope > .card, :scope > .stakeholder-card, :scope > .uc-item'));
|
|
4698
|
+
var index = siblings.indexOf(card);
|
|
4699
|
+
card.appendChild(createCommentUI(sectionId, index));
|
|
4700
|
+
});
|
|
4701
|
+
|
|
4702
|
+
// Module spec lists: nested items in ucList, brList, entList containers
|
|
4703
|
+
document.querySelectorAll('[id^="ucList-"] > .uc-item, [id^="brList-"] > div, [id^="entList-"] > .entity-block').forEach(function(item) {
|
|
4704
|
+
if (item.dataset.commentInitialized) return;
|
|
4705
|
+
item.dataset.commentInitialized = 'true';
|
|
4706
|
+
var list = item.parentElement;
|
|
4707
|
+
var listId = list.id;
|
|
4708
|
+
var siblings = Array.from(list.children);
|
|
4709
|
+
var index = siblings.indexOf(item);
|
|
4710
|
+
item.appendChild(createCommentUI(listId, index));
|
|
4711
|
+
});
|
|
4712
|
+
|
|
4713
|
+
// Stakeholder cards in grid
|
|
4714
|
+
document.querySelectorAll('.stakeholder-grid > .stakeholder-card').forEach(function(card) {
|
|
4715
|
+
if (card.dataset.commentInitialized) return;
|
|
4716
|
+
card.dataset.commentInitialized = 'true';
|
|
4717
|
+
var grid = card.parentElement;
|
|
4718
|
+
var section = card.closest('.section');
|
|
4719
|
+
var sectionId = section ? section.id : 'stakeholders';
|
|
4720
|
+
var siblings = Array.from(grid.children);
|
|
4721
|
+
var index = siblings.indexOf(card);
|
|
4722
|
+
card.appendChild(createCommentUI(sectionId, index));
|
|
4723
|
+
});
|
|
4724
|
+
|
|
4725
|
+
// Scope items
|
|
4726
|
+
['scopeVital', 'scopeImportant', 'scopeOptional', 'scopeExcluded'].forEach(function(containerId) {
|
|
4727
|
+
var container = document.getElementById(containerId);
|
|
4728
|
+
if (!container) return;
|
|
4729
|
+
container.querySelectorAll('.uc-item').forEach(function(item, index) {
|
|
4730
|
+
if (item.dataset.commentInitialized) return;
|
|
4731
|
+
item.dataset.commentInitialized = 'true';
|
|
4732
|
+
item.appendChild(createCommentUI(containerId, index));
|
|
4733
|
+
});
|
|
4734
|
+
});
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4737
|
+
function createCommentUI(sectionId, cardIndex) {
|
|
4738
|
+
const comments = getCommentsForCard(sectionId, cardIndex);
|
|
4739
|
+
const count = comments.length;
|
|
4740
|
+
|
|
4741
|
+
const container = document.createElement('div');
|
|
4742
|
+
container.className = 'comment-btn-container';
|
|
4743
|
+
container.dataset.sectionId = sectionId;
|
|
4744
|
+
container.dataset.cardIndex = cardIndex;
|
|
4745
|
+
|
|
4746
|
+
container.innerHTML = `
|
|
4747
|
+
<button class="comment-toggle-btn" onclick="toggleCommentThread('${sectionId}', ${cardIndex})">
|
|
4748
|
+
Commentaires <span class="comment-count ${count === 0 ? 'empty' : ''}">${count}</span>
|
|
4749
|
+
</button>
|
|
4750
|
+
<div class="comment-thread" id="comment-thread-${sectionId}-${cardIndex}">
|
|
4751
|
+
<div class="comment-items" id="comment-items-${sectionId}-${cardIndex}">
|
|
4752
|
+
${renderCommentItems(sectionId, cardIndex)}
|
|
4753
|
+
</div>
|
|
4754
|
+
<div class="comment-add-form">
|
|
4755
|
+
<textarea id="comment-text-${sectionId}-${cardIndex}" placeholder="Ajouter un commentaire..."></textarea>
|
|
4756
|
+
<select id="comment-cat-${sectionId}-${cardIndex}">
|
|
4757
|
+
<option value="clarification">Clarification</option>
|
|
4758
|
+
<option value="correction">Correction</option>
|
|
4759
|
+
<option value="suggestion">Suggestion</option>
|
|
4760
|
+
</select>
|
|
4761
|
+
<button onclick="addInlineComment('${sectionId}', ${cardIndex})">Ajouter</button>
|
|
4762
|
+
</div>
|
|
4763
|
+
</div>
|
|
4764
|
+
`;
|
|
4765
|
+
|
|
4766
|
+
return container;
|
|
4767
|
+
}
|
|
4768
|
+
|
|
4769
|
+
function getCommentsForCard(sectionId, cardIndex) {
|
|
4770
|
+
return (data.comments || []).filter(c =>
|
|
4771
|
+
c.sectionId === sectionId && c.cardIndex === cardIndex
|
|
4772
|
+
);
|
|
4773
|
+
}
|
|
4774
|
+
|
|
4775
|
+
function toggleCommentThread(sectionId, cardIndex) {
|
|
4776
|
+
const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
|
|
4777
|
+
if (thread) thread.classList.toggle('visible');
|
|
4778
|
+
}
|
|
4779
|
+
|
|
4780
|
+
function renderCommentItems(sectionId, cardIndex) {
|
|
4781
|
+
const comments = getCommentsForCard(sectionId, cardIndex);
|
|
4782
|
+
if (comments.length === 0) {
|
|
4783
|
+
return '<div style="font-size:0.8rem;color:var(--text-muted);padding:0.5rem 0;font-style:italic;">Aucun commentaire</div>';
|
|
4784
|
+
}
|
|
4785
|
+
|
|
4786
|
+
return comments.map((c, i) => {
|
|
4787
|
+
const initials = (c.author || 'U').substring(0, 2).toUpperCase();
|
|
4788
|
+
const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '';
|
|
4789
|
+
const globalIndex = data.comments.indexOf(c);
|
|
4790
|
+
|
|
4791
|
+
return `
|
|
4792
|
+
<div class="comment-item">
|
|
4793
|
+
<div class="comment-avatar">${initials}</div>
|
|
4794
|
+
<div class="comment-body">
|
|
4795
|
+
<div class="comment-meta">
|
|
4796
|
+
<span class="comment-author">${c.author || 'Utilisateur'}</span>
|
|
4797
|
+
<span class="comment-date">${date}</span>
|
|
4798
|
+
<span class="comment-category comment-category-${c.category}">${c.category}</span>
|
|
4799
|
+
<span class="comment-status comment-status-${c.status}">${c.status === 'validated' ? 'Valide' : 'A revoir'}</span>
|
|
4800
|
+
</div>
|
|
4801
|
+
<div class="comment-text">${c.content}</div>
|
|
4802
|
+
<div class="comment-actions">
|
|
4803
|
+
<button class="comment-action-btn" onclick="toggleCommentStatus(${globalIndex})">${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}</button>
|
|
4804
|
+
<button class="comment-action-btn" onclick="deleteComment(${globalIndex})" style="color:var(--error);">Supprimer</button>
|
|
4805
|
+
</div>
|
|
4806
|
+
</div>
|
|
4807
|
+
</div>
|
|
4808
|
+
`;
|
|
4809
|
+
}).join('');
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
function addInlineComment(sectionId, cardIndex) {
|
|
4813
|
+
const textEl = document.getElementById('comment-text-' + sectionId + '-' + cardIndex);
|
|
4814
|
+
const catEl = document.getElementById('comment-cat-' + sectionId + '-' + cardIndex);
|
|
4815
|
+
const content = textEl.value.trim();
|
|
4816
|
+
if (!content) return;
|
|
4817
|
+
|
|
4818
|
+
const comment = {
|
|
4819
|
+
id: 'comment-' + Date.now(),
|
|
4820
|
+
sectionId: sectionId,
|
|
4821
|
+
cardIndex: cardIndex,
|
|
4822
|
+
author: 'Utilisateur',
|
|
4823
|
+
timestamp: new Date().toISOString(),
|
|
4824
|
+
content: content,
|
|
4825
|
+
status: 'to-review',
|
|
4826
|
+
category: catEl.value
|
|
4827
|
+
};
|
|
4828
|
+
|
|
4829
|
+
data.comments.push(comment);
|
|
4830
|
+
textEl.value = '';
|
|
4831
|
+
|
|
4832
|
+
refreshCommentUI(sectionId, cardIndex);
|
|
4833
|
+
renderReviewPanel();
|
|
4834
|
+
autoSave();
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
function toggleCommentStatus(globalIndex) {
|
|
4838
|
+
const comment = data.comments[globalIndex];
|
|
4839
|
+
if (!comment) return;
|
|
4840
|
+
comment.status = comment.status === 'validated' ? 'to-review' : 'validated';
|
|
4841
|
+
refreshCommentUI(comment.sectionId, comment.cardIndex);
|
|
4842
|
+
renderReviewPanel();
|
|
4843
|
+
autoSave();
|
|
4844
|
+
}
|
|
4845
|
+
|
|
4846
|
+
function deleteComment(globalIndex) {
|
|
4847
|
+
const comment = data.comments[globalIndex];
|
|
4848
|
+
if (!comment) return;
|
|
4849
|
+
const sectionId = comment.sectionId;
|
|
4850
|
+
const cardIndex = comment.cardIndex;
|
|
4851
|
+
data.comments.splice(globalIndex, 1);
|
|
4852
|
+
refreshCommentUI(sectionId, cardIndex);
|
|
4853
|
+
renderReviewPanel();
|
|
4854
|
+
autoSave();
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4857
|
+
function refreshCommentUI(sectionId, cardIndex) {
|
|
4858
|
+
// Update comment items
|
|
4859
|
+
const itemsContainer = document.getElementById('comment-items-' + sectionId + '-' + cardIndex);
|
|
4860
|
+
if (itemsContainer) {
|
|
4861
|
+
itemsContainer.innerHTML = renderCommentItems(sectionId, cardIndex);
|
|
4862
|
+
}
|
|
4863
|
+
|
|
4864
|
+
// Update count badge
|
|
4865
|
+
const count = getCommentsForCard(sectionId, cardIndex).length;
|
|
4866
|
+
const container = document.querySelector(`.comment-btn-container[data-section-id="${sectionId}"][data-card-index="${cardIndex}"]`);
|
|
4867
|
+
if (container) {
|
|
4868
|
+
const badge = container.querySelector('.comment-count');
|
|
4869
|
+
if (badge) {
|
|
4870
|
+
badge.textContent = count;
|
|
4871
|
+
badge.className = 'comment-count' + (count === 0 ? ' empty' : '');
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4979
4875
|
|
|
4980
4876
|
|
|
4981
4877
|
/* --- 11-review-panel.js --- */
|
|
4982
|
-
/* ============================================
|
|
4983
|
-
REVIEW PANEL
|
|
4984
|
-
============================================ */
|
|
4985
|
-
let reviewPanelOpen = false;
|
|
4986
|
-
let reviewFilter = 'all';
|
|
4987
|
-
|
|
4988
|
-
function toggleReviewPanel() {
|
|
4989
|
-
reviewPanelOpen = !reviewPanelOpen;
|
|
4990
|
-
const panel = document.getElementById('reviewPanel');
|
|
4991
|
-
const body = document.getElementById('appBody');
|
|
4992
|
-
const btn = document.getElementById('reviewToggleBtn');
|
|
4993
|
-
|
|
4994
|
-
if (reviewPanelOpen) {
|
|
4995
|
-
panel.classList.add('visible');
|
|
4996
|
-
body.classList.add('review-open');
|
|
4997
|
-
btn.classList.add('active');
|
|
4998
|
-
} else {
|
|
4999
|
-
panel.classList.remove('visible');
|
|
5000
|
-
body.classList.remove('review-open');
|
|
5001
|
-
btn.classList.remove('active');
|
|
5002
|
-
}
|
|
5003
|
-
|
|
5004
|
-
renderReviewPanel();
|
|
5005
|
-
}
|
|
5006
|
-
|
|
5007
|
-
function filterReviewComments(filter) {
|
|
5008
|
-
reviewFilter = filter;
|
|
5009
|
-
// Update active filter button
|
|
5010
|
-
document.querySelectorAll('.review-filter-btn').forEach(btn => {
|
|
5011
|
-
btn.classList.toggle('active', btn.dataset.filter === filter);
|
|
5012
|
-
});
|
|
5013
|
-
renderReviewPanel();
|
|
5014
|
-
}
|
|
5015
|
-
|
|
5016
|
-
function renderReviewPanel() {
|
|
5017
|
-
const comments = data.comments || [];
|
|
5018
|
-
|
|
5019
|
-
// Update header badge
|
|
5020
|
-
const toReviewCount = comments.filter(c => c.status === 'to-review').length;
|
|
5021
|
-
const badge = document.getElementById('reviewBadge');
|
|
5022
|
-
if (badge) {
|
|
5023
|
-
badge.textContent = toReviewCount;
|
|
5024
|
-
badge.classList.toggle('hidden', toReviewCount === 0);
|
|
5025
|
-
}
|
|
5026
|
-
|
|
5027
|
-
// Update stats
|
|
5028
|
-
const totalEl = document.getElementById('reviewStatTotal');
|
|
5029
|
-
const toReviewEl = document.getElementById('reviewStatToReview');
|
|
5030
|
-
const validatedEl = document.getElementById('reviewStatValidated');
|
|
5031
|
-
if (totalEl) totalEl.textContent = comments.length;
|
|
5032
|
-
if (toReviewEl) toReviewEl.textContent = toReviewCount;
|
|
5033
|
-
if (validatedEl) validatedEl.textContent = comments.filter(c => c.status === 'validated').length;
|
|
5034
|
-
|
|
5035
|
-
// Filter comments
|
|
5036
|
-
let filtered = comments;
|
|
5037
|
-
if (reviewFilter === 'to-review') {
|
|
5038
|
-
filtered = comments.filter(c => c.status === 'to-review');
|
|
5039
|
-
} else if (reviewFilter === 'validated') {
|
|
5040
|
-
filtered = comments.filter(c => c.status === 'validated');
|
|
5041
|
-
}
|
|
5042
|
-
|
|
5043
|
-
// Render comment list
|
|
5044
|
-
const container = document.getElementById('reviewCommentsList');
|
|
5045
|
-
if (!container) return;
|
|
5046
|
-
|
|
5047
|
-
if (filtered.length === 0) {
|
|
5048
|
-
container.innerHTML = '<div class="review-empty">Aucun commentaire' +
|
|
5049
|
-
(reviewFilter !== 'all' ? ' avec ce filtre' : '') + '.</div>';
|
|
5050
|
-
return;
|
|
5051
|
-
}
|
|
5052
|
-
|
|
5053
|
-
container.innerHTML = filtered.map((c, i) => {
|
|
5054
|
-
const globalIndex = data.comments.indexOf(c);
|
|
5055
|
-
const sectionLabel = getSectionLabel(c.sectionId);
|
|
5056
|
-
const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' }) : '';
|
|
5057
|
-
|
|
5058
|
-
return `
|
|
5059
|
-
<div class="review-comment-item" onclick="navigateToComment('${c.sectionId}', ${c.cardIndex})">
|
|
5060
|
-
<div class="review-comment-section">${sectionLabel}</div>
|
|
5061
|
-
<div class="review-comment-text">${c.content}</div>
|
|
5062
|
-
<div class="review-comment-footer">
|
|
5063
|
-
<span class="review-comment-author">${c.author || 'Utilisateur'} - ${date}</span>
|
|
5064
|
-
<div class="review-comment-actions">
|
|
5065
|
-
<button class="review-action-btn ${c.status === 'validated' ? 'reject' : 'validate'}"
|
|
5066
|
-
onclick="event.stopPropagation();toggleCommentStatus(${globalIndex})"
|
|
5067
|
-
title="${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}">
|
|
5068
|
-
${c.status === 'validated' ? 'A revoir' : 'Valider'}
|
|
5069
|
-
</button>
|
|
5070
|
-
<button class="review-action-btn delete"
|
|
5071
|
-
onclick="event.stopPropagation();deleteComment(${globalIndex})"
|
|
5072
|
-
title="Supprimer">
|
|
5073
|
-
✕
|
|
5074
|
-
</button>
|
|
5075
|
-
</div>
|
|
5076
|
-
</div>
|
|
5077
|
-
</div>
|
|
5078
|
-
`;
|
|
5079
|
-
}).join('');
|
|
5080
|
-
}
|
|
5081
|
-
|
|
5082
|
-
function getSectionLabel(sectionId) {
|
|
5083
|
-
const labels = {
|
|
5084
|
-
'cadrage-context': 'Contexte',
|
|
5085
|
-
'cadrage-stakeholders': 'Parties prenantes',
|
|
5086
|
-
'cadrage-scope': 'Perimetre',
|
|
5087
|
-
'cadrage-
|
|
5088
|
-
'
|
|
5089
|
-
'decomp-
|
|
5090
|
-
'
|
|
5091
|
-
'consol-
|
|
5092
|
-
'consol-
|
|
5093
|
-
'
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
if (
|
|
5097
|
-
|
|
5098
|
-
const
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
}
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
const
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
const
|
|
5113
|
-
const
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
}
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
}
|
|
4878
|
+
/* ============================================
|
|
4879
|
+
REVIEW PANEL
|
|
4880
|
+
============================================ */
|
|
4881
|
+
let reviewPanelOpen = false;
|
|
4882
|
+
let reviewFilter = 'all';
|
|
4883
|
+
|
|
4884
|
+
function toggleReviewPanel() {
|
|
4885
|
+
reviewPanelOpen = !reviewPanelOpen;
|
|
4886
|
+
const panel = document.getElementById('reviewPanel');
|
|
4887
|
+
const body = document.getElementById('appBody');
|
|
4888
|
+
const btn = document.getElementById('reviewToggleBtn');
|
|
4889
|
+
|
|
4890
|
+
if (reviewPanelOpen) {
|
|
4891
|
+
panel.classList.add('visible');
|
|
4892
|
+
body.classList.add('review-open');
|
|
4893
|
+
btn.classList.add('active');
|
|
4894
|
+
} else {
|
|
4895
|
+
panel.classList.remove('visible');
|
|
4896
|
+
body.classList.remove('review-open');
|
|
4897
|
+
btn.classList.remove('active');
|
|
4898
|
+
}
|
|
4899
|
+
|
|
4900
|
+
renderReviewPanel();
|
|
4901
|
+
}
|
|
4902
|
+
|
|
4903
|
+
function filterReviewComments(filter) {
|
|
4904
|
+
reviewFilter = filter;
|
|
4905
|
+
// Update active filter button
|
|
4906
|
+
document.querySelectorAll('.review-filter-btn').forEach(btn => {
|
|
4907
|
+
btn.classList.toggle('active', btn.dataset.filter === filter);
|
|
4908
|
+
});
|
|
4909
|
+
renderReviewPanel();
|
|
4910
|
+
}
|
|
4911
|
+
|
|
4912
|
+
function renderReviewPanel() {
|
|
4913
|
+
const comments = data.comments || [];
|
|
4914
|
+
|
|
4915
|
+
// Update header badge
|
|
4916
|
+
const toReviewCount = comments.filter(c => c.status === 'to-review').length;
|
|
4917
|
+
const badge = document.getElementById('reviewBadge');
|
|
4918
|
+
if (badge) {
|
|
4919
|
+
badge.textContent = toReviewCount;
|
|
4920
|
+
badge.classList.toggle('hidden', toReviewCount === 0);
|
|
4921
|
+
}
|
|
4922
|
+
|
|
4923
|
+
// Update stats
|
|
4924
|
+
const totalEl = document.getElementById('reviewStatTotal');
|
|
4925
|
+
const toReviewEl = document.getElementById('reviewStatToReview');
|
|
4926
|
+
const validatedEl = document.getElementById('reviewStatValidated');
|
|
4927
|
+
if (totalEl) totalEl.textContent = comments.length;
|
|
4928
|
+
if (toReviewEl) toReviewEl.textContent = toReviewCount;
|
|
4929
|
+
if (validatedEl) validatedEl.textContent = comments.filter(c => c.status === 'validated').length;
|
|
4930
|
+
|
|
4931
|
+
// Filter comments
|
|
4932
|
+
let filtered = comments;
|
|
4933
|
+
if (reviewFilter === 'to-review') {
|
|
4934
|
+
filtered = comments.filter(c => c.status === 'to-review');
|
|
4935
|
+
} else if (reviewFilter === 'validated') {
|
|
4936
|
+
filtered = comments.filter(c => c.status === 'validated');
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
// Render comment list
|
|
4940
|
+
const container = document.getElementById('reviewCommentsList');
|
|
4941
|
+
if (!container) return;
|
|
4942
|
+
|
|
4943
|
+
if (filtered.length === 0) {
|
|
4944
|
+
container.innerHTML = '<div class="review-empty">Aucun commentaire' +
|
|
4945
|
+
(reviewFilter !== 'all' ? ' avec ce filtre' : '') + '.</div>';
|
|
4946
|
+
return;
|
|
4947
|
+
}
|
|
4948
|
+
|
|
4949
|
+
container.innerHTML = filtered.map((c, i) => {
|
|
4950
|
+
const globalIndex = data.comments.indexOf(c);
|
|
4951
|
+
const sectionLabel = getSectionLabel(c.sectionId);
|
|
4952
|
+
const date = c.timestamp ? new Date(c.timestamp).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' }) : '';
|
|
4953
|
+
|
|
4954
|
+
return `
|
|
4955
|
+
<div class="review-comment-item" onclick="navigateToComment('${c.sectionId}', ${c.cardIndex})">
|
|
4956
|
+
<div class="review-comment-section">${sectionLabel}</div>
|
|
4957
|
+
<div class="review-comment-text">${c.content}</div>
|
|
4958
|
+
<div class="review-comment-footer">
|
|
4959
|
+
<span class="review-comment-author">${c.author || 'Utilisateur'} - ${date}</span>
|
|
4960
|
+
<div class="review-comment-actions">
|
|
4961
|
+
<button class="review-action-btn ${c.status === 'validated' ? 'reject' : 'validate'}"
|
|
4962
|
+
onclick="event.stopPropagation();toggleCommentStatus(${globalIndex})"
|
|
4963
|
+
title="${c.status === 'validated' ? 'Remettre a revoir' : 'Valider'}">
|
|
4964
|
+
${c.status === 'validated' ? 'A revoir' : 'Valider'}
|
|
4965
|
+
</button>
|
|
4966
|
+
<button class="review-action-btn delete"
|
|
4967
|
+
onclick="event.stopPropagation();deleteComment(${globalIndex})"
|
|
4968
|
+
title="Supprimer">
|
|
4969
|
+
✕
|
|
4970
|
+
</button>
|
|
4971
|
+
</div>
|
|
4972
|
+
</div>
|
|
4973
|
+
</div>
|
|
4974
|
+
`;
|
|
4975
|
+
}).join('');
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
function getSectionLabel(sectionId) {
|
|
4979
|
+
const labels = {
|
|
4980
|
+
'cadrage-context': 'Contexte',
|
|
4981
|
+
'cadrage-stakeholders': 'Parties prenantes',
|
|
4982
|
+
'cadrage-scope': 'Perimetre',
|
|
4983
|
+
'cadrage-success': 'Criteres',
|
|
4984
|
+
'decomp-modules': 'Domaines',
|
|
4985
|
+
'decomp-dependencies': 'Dependances',
|
|
4986
|
+
'consol-interactions': 'Interactions',
|
|
4987
|
+
'consol-permissions': 'Acces',
|
|
4988
|
+
'consol-flows': 'Parcours',
|
|
4989
|
+
'handoff-summary': 'Synthese'
|
|
4990
|
+
};
|
|
4991
|
+
if (labels[sectionId]) return labels[sectionId];
|
|
4992
|
+
if (sectionId.startsWith('module-spec-')) {
|
|
4993
|
+
const code = sectionId.replace('module-spec-', '');
|
|
4994
|
+
const mod = data.modules.find(m => m.code === code);
|
|
4995
|
+
return mod ? mod.name : code;
|
|
4996
|
+
}
|
|
4997
|
+
// Handle module structure sections (module-struct-{code}-{section})
|
|
4998
|
+
const structMatch = sectionId.match(/^module-struct-(.+?)-(.+)$/);
|
|
4999
|
+
if (structMatch) {
|
|
5000
|
+
const mod = data.modules.find(m => m.code === structMatch[1]);
|
|
5001
|
+
const modName = mod ? mod.name : structMatch[1];
|
|
5002
|
+
return modName + ' > Structure > ' + structMatch[2];
|
|
5003
|
+
}
|
|
5004
|
+
// Handle list-based sectionIds (ucList-*, brList-*, entList-*)
|
|
5005
|
+
const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
|
|
5006
|
+
if (listMatch) {
|
|
5007
|
+
const tabLabels = { uc: 'Cas d\'utilisation', br: 'Regles metier', ent: 'Donnees' };
|
|
5008
|
+
const mod = data.modules.find(m => m.code === listMatch[2]);
|
|
5009
|
+
const modName = mod ? mod.name : listMatch[2];
|
|
5010
|
+
return modName + ' > ' + (tabLabels[listMatch[1]] || listMatch[1]);
|
|
5011
|
+
}
|
|
5012
|
+
return sectionId;
|
|
5013
|
+
}
|
|
5014
|
+
|
|
5015
|
+
function navigateToComment(sectionId, cardIndex) {
|
|
5016
|
+
// Handle list-based sectionIds (ucList-*, brList-*, entList-*) → navigate to module + tab
|
|
5017
|
+
const listMatch = sectionId.match(/^(uc|br|ent)List-(.+)$/);
|
|
5018
|
+
if (listMatch) {
|
|
5019
|
+
const [, tabType, moduleCode] = listMatch;
|
|
5020
|
+
showSection('module-spec-' + moduleCode);
|
|
5021
|
+
setTimeout(() => {
|
|
5022
|
+
switchTab(moduleCode, tabType);
|
|
5023
|
+
scrollToCommentThread(sectionId, cardIndex);
|
|
5024
|
+
}, 150);
|
|
5025
|
+
} else {
|
|
5026
|
+
showSection(sectionId);
|
|
5027
|
+
scrollToCommentThread(sectionId, cardIndex);
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
function scrollToCommentThread(sectionId, cardIndex) {
|
|
5032
|
+
setTimeout(() => {
|
|
5033
|
+
const thread = document.getElementById('comment-thread-' + sectionId + '-' + cardIndex);
|
|
5034
|
+
if (thread && !thread.classList.contains('visible')) {
|
|
5035
|
+
thread.classList.add('visible');
|
|
5036
|
+
}
|
|
5037
|
+
const container = document.querySelector(`.comment-btn-container[data-section-id="${sectionId}"][data-card-index="${cardIndex}"]`);
|
|
5038
|
+
if (container) {
|
|
5039
|
+
container.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
5040
|
+
}
|
|
5041
|
+
}, 100);
|
|
5042
|
+
}
|
|
5148
5043
|
|
|
5149
5044
|
</script>
|
|
5150
5045
|
</body>
|