@@ -635,6 +635,167 @@ const Hooks = {
635
635
} ) ;
636
636
} ,
637
637
} ,
638
+ CompensationStrengthIndicator : {
639
+ mounted ( ) {
640
+ const input = this . el . querySelector ( "input[type='text']" ) ;
641
+ const strengthBar = this . el . querySelector ( "[data-strength-bar]" ) ;
642
+ const strengthLabel = this . el . querySelector ( "[data-strength-label]" ) ;
643
+
644
+ if ( ! input || ! strengthBar || ! strengthLabel ) return ;
645
+
646
+ const minAmount = 50000 ;
647
+
648
+ const expandShorthand = ( value : string ) : string => {
649
+ const trimmed = value . trim ( ) . toLowerCase ( ) ;
650
+
651
+ // Handle 'k' for thousands (e.g., "100k" -> "100000")
652
+ if ( trimmed . endsWith ( "k" ) ) {
653
+ const number = parseFloat ( trimmed . slice ( 0 , - 1 ) ) ;
654
+ if ( ! isNaN ( number ) ) {
655
+ return Math . floor ( number * 1000 ) . toString ( ) ;
656
+ }
657
+ }
658
+
659
+ // Handle 'm' for millions (e.g., "1m" -> "1000000")
660
+ if ( trimmed . endsWith ( "m" ) ) {
661
+ const number = parseFloat ( trimmed . slice ( 0 , - 1 ) ) ;
662
+ if ( ! isNaN ( number ) ) {
663
+ return Math . floor ( number * 1000000 ) . toString ( ) ;
664
+ }
665
+ }
666
+
667
+ // Return just the digits if no shorthand
668
+ return value . replace ( / [ ^ 0 - 9 ] / g, "" ) ;
669
+ } ;
670
+
671
+ const formatWithCommas = ( value : string ) : string => {
672
+ // First expand any shorthand notation
673
+ const expanded = expandShorthand ( value ) ;
674
+ // Add commas for thousands separators
675
+ return expanded . replace ( / \B (? = ( \d { 3 } ) + (? ! \d ) ) / g, "," ) ;
676
+ } ;
677
+
678
+ const updateStrength = ( ) => {
679
+ const value = expandShorthand ( input . value ) ;
680
+ const amount = parseInt ( value ) || 0 ;
681
+
682
+ let strength = 0 ;
683
+ let label = "" ;
684
+ let color = "bg-gray-200" ;
685
+
686
+ if ( amount >= 500000 ) {
687
+ strength = 99 ;
688
+ label = "Big D Energy" ;
689
+ color = "bg-emerald-500" ;
690
+ } else if ( amount >= 400000 ) {
691
+ strength = 90 ;
692
+ label = "Baller Status" ;
693
+ color = "bg-emerald-500" ;
694
+ } else if ( amount >= 300000 ) {
695
+ strength = 80 ;
696
+ label = "High Roller" ;
697
+ color = "bg-emerald-500" ;
698
+ } else if ( amount >= 200000 ) {
699
+ strength = 70 ;
700
+ label = "Big League" ;
701
+ color = "bg-emerald-500" ;
702
+ } else if ( amount >= 150000 ) {
703
+ strength = 60 ;
704
+ label = "Major League" ;
705
+ color = "bg-emerald-500" ;
706
+ } else if ( amount >= 100000 ) {
707
+ strength = 50 ;
708
+ label = "Six Figures" ;
709
+ color = "bg-emerald-500" ;
710
+ } else if ( amount >= 75000 ) {
711
+ strength = 40 ;
712
+ label = "Solid Pay" ;
713
+ color = "bg-emerald-500" ;
714
+ } else if ( amount >= minAmount ) {
715
+ strength = 30 ;
716
+ label = "Decent" ;
717
+ color = "bg-emerald-500" ;
718
+ }
719
+
720
+ // Update strength bar
721
+ strengthBar . style . width = `${ strength } %` ;
722
+ strengthBar . className = `h-2 rounded-full transition-all duration-300 ${ color } ` ;
723
+
724
+ // Show/hide the entire indicator section
725
+ const indicatorSection = strengthBar . closest ( ".mt-2" ) ;
726
+ if ( amount >= minAmount ) {
727
+ indicatorSection . style . display = "block" ;
728
+ } else {
729
+ indicatorSection . style . display = "none" ;
730
+ }
731
+
732
+ // Update label
733
+ strengthLabel . textContent = label ;
734
+ strengthLabel . className = `text-sm font-medium transition-colors duration-300 ${
735
+ strength >= 80
736
+ ? "text-emerald-500"
737
+ : strength >= 60
738
+ ? "text-emerald-500"
739
+ : strength >= 40
740
+ ? "text-emerald-500"
741
+ : strength >= 20
742
+ ? "text-emerald-500"
743
+ : "text-gray-600"
744
+ } `;
745
+ } ;
746
+
747
+ const handleInput = ( e : Event ) => {
748
+ const target = e . target as HTMLInputElement ;
749
+ const cursorPosition = target . selectionStart || 0 ;
750
+ const oldValue = target . value ;
751
+
752
+ // Check if user just typed 'k' or 'm' to trigger expansion
753
+ const shouldExpand =
754
+ oldValue . toLowerCase ( ) . endsWith ( "k" ) ||
755
+ oldValue . toLowerCase ( ) . endsWith ( "m" ) ;
756
+
757
+ let formattedValue : string ;
758
+ let newCursorPosition = cursorPosition ;
759
+
760
+ if ( shouldExpand ) {
761
+ // Expand shorthand and format with commas
762
+ formattedValue = formatWithCommas ( oldValue ) ;
763
+ // Place cursor at the end after expansion
764
+ newCursorPosition = formattedValue . length ;
765
+ } else {
766
+ // Just format with commas, preserving user input
767
+ const digitsOnly = oldValue . replace ( / [ ^ 0 - 9 ] / g, "" ) ;
768
+ formattedValue = digitsOnly . replace ( / \B (? = ( \d { 3 } ) + (? ! \d ) ) / g, "," ) ;
769
+
770
+ // Adjust cursor position to account for added/removed commas
771
+ const oldCommas = ( oldValue . match ( / , / g) || [ ] ) . length ;
772
+ const newCommas = ( formattedValue . match ( / , / g) || [ ] ) . length ;
773
+ newCursorPosition = cursorPosition + ( newCommas - oldCommas ) ;
774
+ }
775
+
776
+ // Only update if the value changed to prevent cursor jumping
777
+ if ( oldValue !== formattedValue ) {
778
+ target . value = formattedValue ;
779
+
780
+ // Set cursor position after the DOM updates
781
+ setTimeout ( ( ) => {
782
+ target . setSelectionRange ( newCursorPosition , newCursorPosition ) ;
783
+ } , 0 ) ;
784
+ }
785
+
786
+ updateStrength ( ) ;
787
+ } ;
788
+
789
+ input . addEventListener ( "input" , handleInput ) ;
790
+ input . addEventListener ( "keyup" , updateStrength ) ;
791
+
792
+ // Initial formatting and update
793
+ if ( input . value ) {
794
+ input . value = formatWithCommas ( input . value ) ;
795
+ }
796
+ updateStrength ( ) ;
797
+ } ,
798
+ } ,
638
799
} satisfies Record < string , Partial < ViewHook > & Record < string , unknown > > ;
639
800
640
801
// Accessible focus handling
0 commit comments