Skip to content

Commit 0957ce4

Browse files
committed
fix: updating credentials alerts and management patterns
1 parent ec087d7 commit 0957ce4

File tree

10 files changed

+615
-153
lines changed

10 files changed

+615
-153
lines changed

src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import OsmlTray from "@/components/OsmlTray";
1010
import StatusDisplay from "@/components/StatusDisplay";
1111
import Logo from "@/components/Logo";
1212
import FeaturePopup, { type FeaturePopupData } from "@/components/FeaturePopup";
13+
import ConfigWarnings from "@/components/alert/ConfigWarnings";
1314
import { ResourceProvider } from "@/context/ResourceContext";
1415

1516
/** Natural Earth II fallback (offline, bundled with Cesium) */
@@ -81,6 +82,8 @@ const App = () => {
8182
if (!baseLayer) return null;
8283

8384
return (
85+
<>
86+
<ConfigWarnings />
8487
<ResiumViewer
8588
ref={viewerRef}
8689
full
@@ -113,6 +116,7 @@ const App = () => {
113116
)}
114117
</ResourceProvider>
115118
</ResiumViewer>
119+
</>
116120
);
117121
};
118122

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* Copyright 2023-2026 Amazon.com, Inc. or its affiliates. */
2+
3+
4+
/* ── ConfigWarnings toast stack ─────────────────────────────────── */
5+
6+
.cw-stack {
7+
position: fixed;
8+
top: 16px;
9+
left: 50%;
10+
transform: translateX(-50%);
11+
z-index: 3000;
12+
display: flex;
13+
flex-direction: column;
14+
gap: 10px;
15+
pointer-events: none;
16+
max-width: 520px;
17+
width: 90vw;
18+
}
19+
20+
/* ── Individual toast ──────────────────────────────────────────── */
21+
22+
.cw-toast {
23+
pointer-events: auto;
24+
display: flex;
25+
align-items: flex-start;
26+
gap: 12px;
27+
padding: 14px 16px;
28+
border-radius: 12px;
29+
border: 1px solid;
30+
backdrop-filter: blur(16px);
31+
-webkit-backdrop-filter: blur(16px);
32+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
33+
animation: cw-slide-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) both;
34+
}
35+
36+
.cw-toast--exit {
37+
animation: cw-slide-out 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
38+
}
39+
40+
/* ── Content ───────────────────────────────────────────────────── */
41+
42+
.cw-toast-body {
43+
flex: 1;
44+
min-width: 0;
45+
}
46+
47+
.cw-toast-title {
48+
font-size: 13px;
49+
font-weight: 650;
50+
margin-bottom: 2px;
51+
}
52+
53+
.cw-toast-message {
54+
font-size: 12px;
55+
line-height: 1.45;
56+
opacity: 0.72;
57+
}
58+
59+
/* ── Action button (e.g. Retry) ────────────────────────────────── */
60+
61+
.cw-toast-action {
62+
flex-shrink: 0;
63+
background: rgba(220, 38, 38, 0.2);
64+
border: 1px solid rgba(220, 38, 38, 0.3);
65+
border-radius: 8px;
66+
color: #fca5a5;
67+
padding: 6px 14px;
68+
font-size: 12px;
69+
font-weight: 600;
70+
cursor: pointer;
71+
transition: background 0.15s ease;
72+
white-space: nowrap;
73+
align-self: center;
74+
}
75+
76+
.cw-toast-action:hover {
77+
background: rgba(220, 38, 38, 0.35);
78+
}
79+
80+
.cw-toast-action:disabled {
81+
opacity: 0.5;
82+
cursor: default;
83+
}
84+
85+
/* ── Close button ──────────────────────────────────────────────── */
86+
87+
.cw-toast-close {
88+
flex-shrink: 0;
89+
background: none;
90+
border: none;
91+
padding: 2px;
92+
cursor: pointer;
93+
opacity: 0.5;
94+
transition: opacity 0.15s ease;
95+
margin-top: 1px;
96+
}
97+
98+
.cw-toast-close:hover {
99+
opacity: 1;
100+
}
101+
102+
/* ── Animations ────────────────────────────────────────────────── */
103+
104+
@keyframes cw-slide-in {
105+
from {
106+
opacity: 0;
107+
transform: translateY(-12px) scale(0.96);
108+
}
109+
to {
110+
opacity: 1;
111+
transform: translateY(0) scale(1);
112+
}
113+
}
114+
115+
@keyframes cw-slide-out {
116+
from {
117+
opacity: 1;
118+
transform: translateY(0) scale(1);
119+
}
120+
to {
121+
opacity: 0;
122+
transform: translateY(-8px) scale(0.96);
123+
}
124+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2023-2026 Amazon.com, Inc. or its affiliates.
2+
3+
import { useEffect, useState } from "react";
4+
import { getConfigWarnings, type ConfigWarning } from "@/config";
5+
import "./ConfigWarnings.css";
6+
7+
/** Auto-dismiss delay in ms per severity */
8+
const DISMISS_DELAY: Record<ConfigWarning["severity"], number> = {
9+
warning: 12_000,
10+
error: 20_000
11+
};
12+
13+
const SEVERITY_STYLES: Record<
14+
ConfigWarning["severity"],
15+
{ accent: string; bg: string; border: string; icon: string }
16+
> = {
17+
error: {
18+
accent: "#fca5a5",
19+
bg: "rgba(220, 38, 38, 0.12)",
20+
border: "rgba(220, 38, 38, 0.3)",
21+
icon: "#f87171"
22+
},
23+
warning: {
24+
accent: "#fcd34d",
25+
bg: "rgba(234, 179, 8, 0.10)",
26+
border: "rgba(234, 179, 8, 0.3)",
27+
icon: "#facc15"
28+
}
29+
};
30+
31+
function WarningIcon({ color }: { color: string }) {
32+
return (
33+
<svg
34+
width="20"
35+
height="20"
36+
viewBox="0 0 20 20"
37+
fill="none"
38+
style={{ flexShrink: 0 }}
39+
>
40+
<path
41+
d="M10 2L1 18h18L10 2z"
42+
stroke={color}
43+
strokeWidth="1.5"
44+
strokeLinejoin="round"
45+
/>
46+
<path
47+
d="M10 8v4"
48+
stroke={color}
49+
strokeWidth="1.5"
50+
strokeLinecap="round"
51+
/>
52+
<circle cx="10" cy="15" r="0.8" fill={color} />
53+
</svg>
54+
);
55+
}
56+
57+
function ErrorIcon({ color }: { color: string }) {
58+
return (
59+
<svg
60+
width="20"
61+
height="20"
62+
viewBox="0 0 20 20"
63+
fill="none"
64+
style={{ flexShrink: 0 }}
65+
>
66+
<circle cx="10" cy="10" r="8.5" stroke={color} strokeWidth="1.5" />
67+
<path
68+
d="M10 6v5"
69+
stroke={color}
70+
strokeWidth="1.5"
71+
strokeLinecap="round"
72+
/>
73+
<circle cx="10" cy="14" r="0.8" fill={color} />
74+
</svg>
75+
);
76+
}
77+
78+
function Toast({
79+
warning,
80+
onDismiss
81+
}: {
82+
warning: ConfigWarning;
83+
onDismiss: () => void;
84+
}) {
85+
const [exiting, setExiting] = useState(false);
86+
const s = SEVERITY_STYLES[warning.severity];
87+
88+
useEffect(() => {
89+
const timer = setTimeout(() => {
90+
setExiting(true);
91+
}, DISMISS_DELAY[warning.severity]);
92+
return () => clearTimeout(timer);
93+
}, [warning.severity]);
94+
95+
// After the exit animation finishes, remove the toast
96+
useEffect(() => {
97+
if (!exiting) return;
98+
const timer = setTimeout(onDismiss, 300);
99+
return () => clearTimeout(timer);
100+
}, [exiting, onDismiss]);
101+
102+
const handleDismiss = () => {
103+
setExiting(true);
104+
};
105+
106+
return (
107+
<div
108+
className={`cw-toast ${exiting ? "cw-toast--exit" : ""}`}
109+
style={{
110+
background: s.bg,
111+
borderColor: s.border
112+
}}
113+
>
114+
{warning.severity === "error" ? (
115+
<ErrorIcon color={s.icon} />
116+
) : (
117+
<WarningIcon color={s.icon} />
118+
)}
119+
120+
<div className="cw-toast-body">
121+
<div className="cw-toast-title" style={{ color: s.accent }}>
122+
{warning.title}
123+
</div>
124+
<div
125+
className="cw-toast-message"
126+
style={{ color: s.accent }}
127+
>
128+
{warning.message}
129+
</div>
130+
</div>
131+
132+
<button
133+
className="cw-toast-close"
134+
onClick={handleDismiss}
135+
style={{ color: s.accent }}
136+
aria-label="Dismiss"
137+
>
138+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
139+
<path
140+
d="M3 3l8 8M11 3l-8 8"
141+
stroke="currentColor"
142+
strokeWidth="1.5"
143+
strokeLinecap="round"
144+
/>
145+
</svg>
146+
</button>
147+
</div>
148+
);
149+
}
150+
151+
const ConfigWarnings = () => {
152+
const [warnings, setWarnings] = useState<ConfigWarning[]>([]);
153+
154+
useEffect(() => {
155+
let cancelled = false;
156+
getConfigWarnings().then((result) => {
157+
if (!cancelled) setWarnings(result);
158+
});
159+
return () => { cancelled = true; };
160+
}, []);
161+
162+
const dismiss = (index: number) => {
163+
setWarnings((prev) => prev.filter((_, i) => i !== index));
164+
};
165+
166+
if (warnings.length === 0) return null;
167+
168+
return (
169+
<div className="cw-stack">
170+
{warnings.map((w, i) => (
171+
<Toast key={`${w.severity}-${w.title}`} warning={w} onDismiss={() => dismiss(i)} />
172+
))}
173+
</div>
174+
);
175+
};
176+
177+
export default ConfigWarnings;

0 commit comments

Comments
 (0)