Reorganiza gestão de usuários e remove dados mock

This commit is contained in:
Esdras Renan 2025-10-18 01:15:15 -03:00
parent 630110bf3a
commit dded6d1927
20 changed files with 1863 additions and 1368 deletions

View file

@ -5,6 +5,7 @@ import { ChangeEvent, useEffect, useMemo, useState } from "react"
import { cn } from "@/lib/utils"
import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { COUNTRY_DIAL_CODES, type CountryDialCode } from "@/lib/country-codes"
export type CountryOption = {
code: string
@ -15,26 +16,7 @@ export type CountryOption = {
formatLocal: (digits: string) => string
}
const COUNTRY_OPTIONS: CountryOption[] = [
{
code: "BR",
label: "Brasil",
dialCode: "+55",
flag: "🇧🇷",
maxDigits: 11,
formatLocal: formatBrazilPhone,
},
{
code: "US",
label: "Estados Unidos",
dialCode: "+1",
flag: "🇺🇸",
maxDigits: 10,
formatLocal: formatUsPhone,
},
]
const DEFAULT_COUNTRY = COUNTRY_OPTIONS[0]
const DEFAULT_COUNTRY_CODE = "BR"
function formatBrazilPhone(digits: string): string {
const cleaned = digits.slice(0, 11)
@ -63,12 +45,18 @@ function extractDigits(value?: string | null): string {
function findCountryByValue(value?: string | null): CountryOption {
const digits = extractDigits(value)
return (
COUNTRY_OPTIONS.find((option) => {
const dialDigits = extractDigits(option.dialCode)
return digits.startsWith(dialDigits) && digits.length > dialDigits.length
}) ?? DEFAULT_COUNTRY
)
const dialDigits = extractDigits(DEFAULT_COUNTRY.dialCode)
let matched: CountryOption = DEFAULT_COUNTRY
let matchedLength = digits.startsWith(dialDigits) ? dialDigits.length : 0
for (const option of COUNTRY_OPTIONS) {
const optionDialDigits = extractDigits(option.dialCode)
if (optionDialDigits.length === 0) continue
if (digits.startsWith(optionDialDigits) && optionDialDigits.length > matchedLength) {
matched = option
matchedLength = optionDialDigits.length
}
}
return matched
}
function toLocalDigits(value: string | null | undefined, country: CountryOption): string {
@ -88,9 +76,49 @@ function formatStoredValue(country: CountryOption, localDigits: string): string
function placeholderFor(country: CountryOption): string {
if (country.code === "BR") return "(11) 91234-5678"
if (country.code === "US") return "(555) 123-4567"
if (country.code === "CA") return "(416) 123-4567"
return "Número de telefone"
}
function formatGenericPhone(digits: string): string {
const cleaned = digits.slice(0, 15)
if (!cleaned) return ""
return cleaned.replace(/(\d{3,4})(?=\d)/g, "$1 ").trim()
}
function flagEmojiFor(code: string): string {
if (!code || code.length !== 2) return "🏳️"
const base = 127397
const chars = Array.from(code.toUpperCase()).map((char) => base + char.charCodeAt(0))
return String.fromCodePoint(...chars)
}
const regionDisplayNames =
typeof Intl !== "undefined" && "DisplayNames" in Intl
? new Intl.DisplayNames(["pt-BR", "pt", "en"], { type: "region" })
: null
const SPECIAL_FORMATS: Record<string, { maxDigits: number; formatLocal: (digits: string) => string }> = {
BR: { maxDigits: 11, formatLocal: formatBrazilPhone },
US: { maxDigits: 10, formatLocal: formatUsPhone },
CA: { maxDigits: 10, formatLocal: formatUsPhone },
}
const COUNTRY_OPTIONS: CountryOption[] = COUNTRY_DIAL_CODES.map((entry: CountryDialCode) => {
const special = SPECIAL_FORMATS[entry.code]
return {
code: entry.code,
label: regionDisplayNames?.of(entry.code) ?? entry.name ?? entry.code,
dialCode: entry.dialCode,
flag: flagEmojiFor(entry.code),
maxDigits: special?.maxDigits ?? 15,
formatLocal: special?.formatLocal ?? formatGenericPhone,
}
}).sort((a, b) => a.label.localeCompare(b.label, "pt-BR"))
const DEFAULT_COUNTRY =
COUNTRY_OPTIONS.find((option) => option.code === DEFAULT_COUNTRY_CODE) ?? COUNTRY_OPTIONS[0]
export function PhoneInput({ value, onChange, className, id, name }: PhoneInputProps) {
const [selectedCountry, setSelectedCountry] = useState<CountryOption>(DEFAULT_COUNTRY)
const [localDigits, setLocalDigits] = useState<string>("")
@ -131,7 +159,7 @@ export function PhoneInput({ value, onChange, className, id, name }: PhoneInputP
</span>
</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectContent className="max-h-72 overflow-y-auto">
{COUNTRY_OPTIONS.map((option) => (
<SelectItem key={option.code} value={option.code}>
<span className="inline-flex items-center gap-2">