BranchNumberInput.jsx 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. "use client";
  2. import React from "react";
  3. import {
  4. extractBranchNumberInputFromBranchId,
  5. formatBranchIdFromNumberInput,
  6. normalizeBranchIdInput,
  7. } from "@/lib/frontend/admin/users/userManagementUx";
  8. import { Input } from "@/components/ui/input";
  9. import { Label } from "@/components/ui/label";
  10. export default function BranchNumberInput({
  11. id,
  12. branchId,
  13. onBranchIdChange,
  14. disabled,
  15. invalid = false,
  16. describedBy,
  17. }) {
  18. const [numberDraft, setNumberDraft] = React.useState(() =>
  19. extractBranchNumberInputFromBranchId(branchId),
  20. );
  21. React.useEffect(() => {
  22. const normalizedExternalBranchId = normalizeBranchIdInput(branchId);
  23. const currentDerivedBranchId = formatBranchIdFromNumberInput(numberDraft);
  24. // Keep user typing stable (e.g. "01" stays visible) while syncing true external changes
  25. // like dialog open/reset/user switch.
  26. if (currentDerivedBranchId === normalizedExternalBranchId) return;
  27. setNumberDraft(extractBranchNumberInputFromBranchId(branchId));
  28. }, [branchId, numberDraft]);
  29. const wrapperClass = invalid
  30. ? "border-destructive"
  31. : "border-input";
  32. return (
  33. <div className="grid gap-2">
  34. <Label htmlFor={id}>Niederlassung</Label>
  35. <div
  36. className={`flex h-9 items-stretch overflow-hidden rounded-md border ${wrapperClass}`}
  37. >
  38. <span className="inline-flex shrink-0 items-center border-r bg-muted px-3 text-sm text-muted-foreground">
  39. NL
  40. </span>
  41. <Input
  42. id={id}
  43. type="text"
  44. inputMode="numeric"
  45. pattern="[0-9]*"
  46. value={numberDraft}
  47. onChange={(e) => {
  48. const nextNumberDraft = String(e.target.value || "").replace(
  49. /\D+/g,
  50. "",
  51. );
  52. setNumberDraft(nextNumberDraft);
  53. onBranchIdChange?.(formatBranchIdFromNumberInput(nextNumberDraft));
  54. }}
  55. disabled={disabled}
  56. placeholder="z. B. 01"
  57. className="h-full rounded-none border-0 shadow-none focus-visible:ring-0"
  58. aria-invalid={invalid ? "true" : "false"}
  59. aria-describedby={describedBy}
  60. />
  61. </div>
  62. </div>
  63. );
  64. }