PageSourceSearch

https://sistur.lovable.app/assets/IntegratedTerritorialView-B1sISf_S.js

js sistur.lovable.app collected 2026-10-03 03:49:12 UTC 9,872 bytes, 230 lines download raw bytes

1const e=`import { tx } from '@/i18n/t';
2import { Link } from 'react-router-dom';
3import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
4import { Badge } from '@/components/ui/badge';
5import { Button } from '@/components/ui/button';
6import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
7import { Skeleton } from '@/components/ui/skeleton';
8import { GitBranch, MapPin, AlertTriangle, ArrowRight, Building2, Info } from 'lucide-react';
9import { useIntegratedTerritorialView } from '@/hooks/useIntegratedTerritorialView';
10import { getIntlLocale } from '@/i18n/dateLocale';
11
12interface Props {
13  enterpriseDestinationId: string;
14  enterprisePillarScores: Array<{ pillar: string; score: number; severity: string }>;
15  destinationName: string;
16}
17
18const PILLAR_LABELS: Record<string, string> = {
19  RA: 'I-RA · Relações Ambientais',
20  OE: 'I-OE · Organização Estrutural',
21  AO: 'I-AO · Ações Operacionais',
22};
23
24const SEVERITY_STYLE: Record<string, { color: string; label: string }> = {
25  CRITICO: { color: 'text-severity-critical', label: tx('Crítico') },
26  MODERADO: { color: 'text-severity-moderate', label: tx('Atenção') },
27  BOM: { color: 'text-severity-good', label: tx('Adequado') },
28};
29
30function pct(score: number | undefined) {
31  if (score == null || isNaN(score)) return '—';
32  return \`\${Math.round(score * 100)}%\`;
33}
34
35function delta(ent: number | undefined, terr: number | undefined) {
36  if (ent == null || terr == null) return null;
37  return Math.round((ent - terr) * 100);
38}
39
40export function IntegratedTerritorialView({
41  enterpriseDestinationId,
42  enterprisePillarScores,
43  destinationName,
44}: Props) {
45  const { data, isLoading } = useIntegratedTerritorialView(enterpriseDestinationId, true);
46
47  if (isLoading) {
48    return (
49      <div className="space-y-4">
50        <Skeleton className="h-32 w-full" />
51        <Skeleton className="h-48 w-full" />
52      </div>
53    );
54  }
55
56  if (!data?.matched) {
57    const reasonText =
58      data?.reason === 'no_ibge'
59        ? tx('Este empreendimento não tem código IBGE cadastrado — não é possível localizar o município de referência.')
60        : data?.reason === 'no_assessment'
61        ? tx('Ainda não há nenhum diagnóstico Territorial cadastrado para o município deste empreendimento.')
62        : 'Existe diagnóstico Territorial para o município, mas ele ainda não foi calculado.';
63    return (
64      <Alert>
65        <Info className="h-4 w-4" />
66        <AlertTitle>{tx('Visão Integrada indisponível')}</AlertTitle>
67        <AlertDescription className="space-y-2">
68          <p>{reasonText}</p>
69          <p className="text-sm text-muted-foreground">
70            {tx("A Visão Integrada cruza o desempenho do empreendimento com o diagnóstico Territorial do município, ajudando a separar gargalos internos de causas externas (infra pública, segurança, conectividade, sazonalidade do destino).")}
71          </p>
72        </AlertDescription>
73      </Alert>
74    );
75  }
76
77  const terrPillarMap = new Map(
78    (data.pillarScores || []).map((p) => [p.pillar, p]),
79  );
80  const entPillarMap = new Map(enterprisePillarScores.map((p) => [p.pillar, p]));
81
82  const criticalTerritorialIssues = (data.issues || []).filter(
83    (i) => i.severity === 'CRITICO',
84  );
85
86  return (
87    <div className="space-y-6">
88      {/* Header card with municipality reference */}
89      <Card className="border-primary/30 bg-primary/5">
90        <CardHeader>
91          <div className="flex items-start justify-between gap-3 flex-wrap">
92            <div>
93              <CardTitle className="flex items-center gap-2">
94                <GitBranch className="h-5 w-5 text-primary" />
95                {tx('Visão Integrada — Empreendimento × Município')}
96              </CardTitle>
97              <CardDescription className="mt-1.5 space-y-0.5">
98                <span className="flex items-center gap-1.5">
99                  <Building2 className="h-3.5 w-3.5" /> {destinationName}
100                </span>
101                <span className="flex items-center gap-1.5">
102                  <MapPin className="h-3.5 w-3.5" />
103                  {data.destination?.name} {data.destination?.uf ? \`(\${data.destination.uf})\` : ''}
104                  {data.assessment?.calculated_at && (
105                    <span className="text-xs text-muted-foreground ml-1">
106                      · diagnóstico territorial de{' '}
107                      {new Date(data.assessment.calculated_at).toLocaleDateString(getIntlLocale())}
108                    </span>
109                  )}
110                </span>
111              </CardDescription>
112            </div>
113            <Button asChild variant="outline" size="sm">
114              <Link to={\`/diagnosticos/\${data.assessment!.id}\`}>
115                {tx('Abrir diagnóstico territorial')}
116                <ArrowRight className="h-3.5 w-3.5 ml-1" />
117              </Link>
118            </Button>
119          </div>
120        </CardHeader>
121      </Card>
122
123      {/* Pillar-by-pillar comparison */}
124      <Card>
125        <CardHeader>
126          <CardTitle className="text-base">{tx('Comparação por Pilar')}</CardTitle>
127          <CardDescription>
128            {tx("Score do empreendimento vs. score do município no mesmo pilar sistêmico (RA/OE/AO). Δ positivo indica que o empreendimento está acima do entorno.")}
129          </CardDescription>
130        </CardHeader>
131        <CardContent className="space-y-3">
132          {(['RA', 'OE', 'AO'] as const).map((p) => {
133            const ent = entPillarMap.get(p);
134            const terr = terrPillarMap.get(p);
135            const d = delta(ent?.score, terr?.score);
136            const entSev = ent?.severity ? SEVERITY_STYLE[ent.severity] : null;
137            const terrSev = terr?.severity ? SEVERITY_STYLE[terr.severity] : null;
138            return (
139              <div
140                key={p}
141                className="grid grid-cols-1 md:grid-cols-[1fr_auto_auto_auto] gap-2 md:items-center p-3 rounded-lg border bg-card"
142              >
143                <div className="font-medium text-sm">{tx(PILLAR_LABELS[p])}</div>
144                <div className="text-sm">
145                  <span className="text-muted-foreground">{tx('Empreend.:')} </span>
146                  <span className={\`font-semibold \${entSev?.color ?? ''}\`}>
147                    {pct(ent?.score)} {entSev ? \`· \${entSev.label}\` : ''}
148                  </span>
149                </div>
150                <div className="text-sm">
151                  <span className="text-muted-foreground">{tx('Município:')} </span>
152                  <span className={\`font-semibold \${terrSev?.color ?? ''}\`}>
153                    {pct(terr?.score)} {terrSev ? \`· \${terrSev.label}\` : ''}
154                  </span>
155                </div>
156                <div className="text-sm font-mono">
157                  {d == null ? (
158                    <span className="text-muted-foreground">Δ —</span>
159                  ) : (
160                    <Badge
161                      variant="outline"
162                      className={
163                        d > 5
164                          ? 'border-severity-good/50 text-severity-good'
165                          : d < -5
166                          ? 'border-severity-critical/50 text-severity-critical'
167                          : 'text-muted-foreground'
168                      }
169                    >
170                      Δ {d > 0 ? '+' : ''}
171                      {d} pp
172                    </Badge>
173                  )}
174                </div>
175              </div>
176            );
177          })}
178        </CardContent>
179      </Card>
180
181      {/* Territorial context alerts */}
182      {criticalTerritorialIssues.length > 0 && (
183        <Card className="border-severity-critical/30">
184          <CardHeader>
185            <CardTitle className="text-base flex items-center gap-2">
186              <AlertTriangle className="h-4 w-4 text-severity-critical" />
187              {tx('Gargalos críticos no município')}
188            </CardTitle>
189            <CardDescription>
190              {tx("Esses gargalos territoriais podem afetar o desempenho do empreendimento mesmo quando a gestão interna está adequada — considere ações conjuntas com a secretaria de turismo, consórcio ou trade local.")}
191            </CardDescription>
192          </CardHeader>
193          <CardContent className="space-y-2">
194            {criticalTerritorialIssues.slice(0, 8).map((i) => (
195              <div
196                key={i.id}
197                className="flex items-start gap-3 p-3 rounded-md bg-severity-critical/5 border border-severity-critical/20"
198              >
199                <Badge variant="outline" className="shrink-0 text-xs">
200                  {i.pillar}
201                </Badge>
202                <div className="text-sm">
203                  <div className="font-medium">{tx(i.title)}</div>
204                  {i.description && (
205                    <div className="text-muted-foreground text-xs mt-0.5 line-clamp-2">
206                      {tx(i.description)}
207                    </div>
208                  )}
209                </div>
210              </div>
211            ))}
212            {criticalTerritorialIssues.length > 8 && (
213              <div className="text-xs text-muted-foreground text-center pt-1">
214                + {criticalTerritorialIssues.length - 8} outros gargalos críticos no município
215              </div>
216            )}
217          </CardContent>
218        </Card>
219      )}
220
221      <Alert>
222        <Info className="h-4 w-4" />
223        <AlertDescription className="text-xs">
224          {tx("A Visão Integrada é informativa e não altera o cálculo do diagnóstico Empresarial. Use-a para contextualizar gargalos, justificar planos de ação cruzados (interno + territorial) e priorizar projetos junto à governança do destino.")}
225        </AlertDescription>
226      </Alert>
227    </div>
228  );
229}
230`;export{e as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.