Skip to content

Commit 4fed4fd

Browse files
committed
chore(demo): split user auth from app section
1 parent da0fcb3 commit 4fed4fd

File tree

8 files changed

+139
-80
lines changed

8 files changed

+139
-80
lines changed

examples/build.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ User Section layout (separate section card titled "User", placed after App Secti
442442
2. `LOGIN USER` button:
443443
- Shows `LOGIN USER` when no user is logged in
444444
- Shows `SWITCH USER` when a user is logged in
445-
- Opens modal with empty `External User Id` field
445+
- Opens "Login User" modal with empty `External User Id` field
446446
447447
3. `LOGOUT USER` button (only visible when a user is logged in)
448448
@@ -507,8 +507,8 @@ Send In-App Message Section:
507507
Aliases Section:
508508
509509
- Section title includes info icon (use `MdInfoOutline` from `react-icons/md`).
510-
- List showing key-value pairs (read-only, no delete icons).
511-
- Each item shows: Label | ID.
510+
- Stacked key-value list (read-only, no delete icons).
511+
- Each item shows Label on top, ID below (see styles.md "Stacked" list layout).
512512
- Hide special alias keys from display:
513513
- `external_id`
514514
- `onesignal_id`

examples/demo/src/components/ListWidgets.tsx

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import type { FC } from 'react';
1+
import { type FC, useState } from 'react';
22
import { MdClose } from 'react-icons/md';
33

4+
const COLLAPSE_THRESHOLD = 5;
5+
46
export type PairItem = {
57
key: string;
68
value: string;
@@ -36,7 +38,7 @@ export const PairList: FC<PairListProps> = ({
3638
items.map((item) => (
3739
<div key={item.key} className="list-item two-line">
3840
<div>
39-
<strong>{item.key}</strong>
41+
<span className="list-key">{item.key}</span>
4042
<span>{item.value}</span>
4143
</div>
4244
{onRemove ? (
@@ -60,25 +62,43 @@ export const SingleList: FC<SingleListProps> = ({
6062
items,
6163
emptyText,
6264
onRemove,
63-
}) => (
64-
<div className="card list-card">
65-
{items.length ? (
66-
items.map((item) => (
67-
<div key={item} className="list-item">
68-
<span>{item}</span>
69-
{onRemove ? (
65+
}) => {
66+
const [expanded, setExpanded] = useState(false);
67+
const showAll = expanded || items.length <= COLLAPSE_THRESHOLD;
68+
const displayItems = showAll ? items : items.slice(0, COLLAPSE_THRESHOLD);
69+
const hiddenCount = items.length - COLLAPSE_THRESHOLD;
70+
71+
return (
72+
<div className="card list-card">
73+
{items.length ? (
74+
<>
75+
{displayItems.map((item) => (
76+
<div key={item} className="list-item">
77+
<span>{item}</span>
78+
{onRemove ? (
79+
<button
80+
type="button"
81+
className="delete-btn"
82+
onClick={() => onRemove(item)}
83+
>
84+
<MdClose />
85+
</button>
86+
) : null}
87+
</div>
88+
))}
89+
{!showAll && hiddenCount > 0 && (
7090
<button
7191
type="button"
72-
className="delete-btn"
73-
onClick={() => onRemove(item)}
92+
className="more-link"
93+
onClick={() => setExpanded(true)}
7494
>
75-
<MdClose />
95+
{hiddenCount} more
7696
</button>
77-
) : null}
78-
</div>
79-
))
80-
) : (
81-
<EmptyState text={emptyText} />
82-
)}
83-
</div>
84-
);
97+
)}
98+
</>
99+
) : (
100+
<EmptyState text={emptyText} />
101+
)}
102+
</div>
103+
);
104+
};

examples/demo/src/components/modals/CustomNotificationModal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useEffect, useState } from 'react';
21
import type { FC } from 'react';
2+
import { useEffect, useState } from 'react';
33
import ModalShell from './ModalShell';
44

55
interface CustomNotificationModalProps {
@@ -41,7 +41,7 @@ const CustomNotificationModal: FC<CustomNotificationModalProps> = ({
4141
onChange={(event) => setTitle(event.target.value)}
4242
placeholder="Title"
4343
/>
44-
<textarea
44+
<input
4545
value={body}
4646
onChange={(event) => setBody(event.target.value)}
4747
placeholder="Body"
@@ -50,7 +50,9 @@ const CustomNotificationModal: FC<CustomNotificationModalProps> = ({
5050
<button type="button" onClick={onClose}>
5151
Cancel
5252
</button>
53-
<button type="submit">Send</button>
53+
<button type="submit" disabled={!title.trim() || !body.trim()}>
54+
Send
55+
</button>
5456
</div>
5557
</form>
5658
</ModalShell>

examples/demo/src/components/modals/OutcomeModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const OutcomeModal: FC<OutcomeModalProps> = ({ open, onClose, onSubmit }) => {
2727
<ModalShell open={open}>
2828
<form
2929
autoCapitalize="off"
30-
className="outcome-modal"
3130
onSubmit={(event) => {
3231
event.preventDefault();
3332
const trimmed = name.trim();

examples/demo/src/components/sections/AppSection.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
11
import { IonToggle } from '@ionic/react';
22
import type { FC } from 'react';
3-
import ActionButton from '../ActionButton';
43

54
interface AppSectionProps {
65
appId: string;
76
consentRequired: boolean;
87
privacyConsentGiven: boolean;
9-
externalUserId: string | undefined;
108
onToggleConsent: (checked: boolean) => void;
119
onTogglePrivacyConsent: (checked: boolean) => void;
12-
onLogin: () => void;
13-
onLogout: () => void;
1410
}
1511

1612
const AppSection: FC<AppSectionProps> = ({
1713
appId,
1814
consentRequired,
1915
privacyConsentGiven,
20-
externalUserId,
2116
onToggleConsent,
2217
onTogglePrivacyConsent,
23-
onLogin,
24-
onLogout,
2518
}) => {
26-
const isLoggedIn = Boolean(externalUserId);
27-
2819
return (
2920
<section className="section">
3021
<h2>APP</h2>
@@ -69,27 +60,6 @@ const AppSection: FC<AppSectionProps> = ({
6960
</>
7061
) : null}
7162
</div>
72-
<div className="card kv-card">
73-
<div className="kv-row">
74-
<span>Status</span>
75-
<span className={isLoggedIn ? 'text-success' : undefined}>
76-
{isLoggedIn ? 'Logged In' : 'Anonymous'}
77-
</span>
78-
</div>
79-
<div className="divider" />
80-
<div className="kv-row">
81-
<span>External ID</span>
82-
<span className="id-value">{externalUserId ?? '–'}</span>
83-
</div>
84-
</div>
85-
<ActionButton type="button" onClick={onLogin}>
86-
{isLoggedIn ? 'SWITCH USER' : 'LOGIN USER'}
87-
</ActionButton>
88-
{isLoggedIn ? (
89-
<ActionButton variant="outline" type="button" onClick={onLogout}>
90-
LOGOUT USER
91-
</ActionButton>
92-
) : null}
9363
</section>
9464
);
9565
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { FC } from 'react';
2+
import ActionButton from '../ActionButton';
3+
4+
interface UserSectionProps {
5+
externalUserId: string | undefined;
6+
onLogin: () => void;
7+
onLogout: () => void;
8+
}
9+
10+
const UserSection: FC<UserSectionProps> = ({
11+
externalUserId,
12+
onLogin,
13+
onLogout,
14+
}) => {
15+
const isLoggedIn = Boolean(externalUserId);
16+
17+
return (
18+
<section className="section">
19+
<h2>USER</h2>
20+
<div className="card kv-card">
21+
<div className="kv-row">
22+
<span>Status</span>
23+
<span className={isLoggedIn ? 'text-success' : undefined}>
24+
{isLoggedIn ? 'Logged In' : 'Anonymous'}
25+
</span>
26+
</div>
27+
<div className="divider" />
28+
<div className="kv-row">
29+
<span>External ID</span>
30+
<span className="id-value">{externalUserId ?? '–'}</span>
31+
</div>
32+
</div>
33+
<ActionButton type="button" onClick={onLogin}>
34+
{isLoggedIn ? 'SWITCH USER' : 'LOGIN USER'}
35+
</ActionButton>
36+
{isLoggedIn ? (
37+
<ActionButton variant="outline" type="button" onClick={onLogout}>
38+
LOGOUT USER
39+
</ActionButton>
40+
) : null}
41+
</section>
42+
);
43+
};
44+
45+
export default UserSection;

examples/demo/src/pages/Home.css

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
--radius-card: 12px;
3333
--radius-button: 8px;
3434
--radius-input: 8px;
35-
--radius-modal: 16px;
35+
--radius-modal: 28px;
3636

3737
/* Shadows (elevation 1) */
3838
--shadow-card: 0 2px 1px -1px rgba(0, 0, 0, 0.2),
@@ -58,8 +58,6 @@
5858
display: flex;
5959
align-items: center;
6060
justify-content: center;
61-
font-weight: 700;
62-
letter-spacing: 0.2px;
6361
}
6462

6563
.brand-title {
@@ -77,7 +75,6 @@
7775
font-size: var(--font-size-body);
7876
font-weight: 400;
7977
color: #fff;
80-
opacity: 0.9;
8178
}
8279

8380
.icon-btn {
@@ -280,9 +277,9 @@
280277
flex-direction: column;
281278
}
282279

283-
.list-item.two-line strong {
280+
.list-item.two-line .list-key {
284281
font-size: var(--font-size-body);
285-
font-weight: 500;
282+
color: inherit;
286283
}
287284

288285
.list-item.two-line span {
@@ -316,6 +313,18 @@
316313
font-size: 18px;
317314
}
318315

316+
.more-link {
317+
background: transparent;
318+
border: 0;
319+
color: var(--os-primary);
320+
font-size: var(--font-size-body);
321+
font-weight: 500;
322+
padding: 4px 0;
323+
cursor: pointer;
324+
text-align: center;
325+
width: 100%;
326+
}
327+
319328
.modal-backdrop {
320329
position: fixed;
321330
inset: 0;
@@ -331,23 +340,36 @@
331340
width: min(640px, 100%);
332341
background: var(--os-card-background);
333342
border-radius: var(--radius-modal);
334-
padding: 18px;
343+
padding: 24px;
335344
}
336345

337346
.modal-card h3 {
338347
margin: 0 0 14px;
339-
font-size: 22px;
340-
font-weight: 700;
348+
font-size: 24px;
349+
font-weight: 400;
341350
}
342351

343352
.modal-card input,
344353
.modal-card textarea {
345354
width: 100%;
346-
border: 1px solid #ccd2da;
355+
border: 1px solid var(--os-grey-700);
347356
border-radius: var(--radius-input);
348357
padding: 14px 12px;
349358
font-size: 17px;
350359
margin-bottom: 10px;
360+
color: inherit;
361+
}
362+
363+
.modal-card input:focus,
364+
.modal-card textarea:focus {
365+
outline: 2px solid var(--os-primary);
366+
outline-offset: -1px;
367+
border-color: transparent;
368+
}
369+
370+
.modal-card input::placeholder,
371+
.modal-card textarea::placeholder {
372+
color: var(--os-grey-600);
351373
}
352374

353375
.modal-card textarea {
@@ -407,20 +429,21 @@
407429
.modal-actions {
408430
display: flex;
409431
justify-content: flex-end;
410-
gap: 10px;
432+
gap: 8px;
411433
margin-top: 24px;
412434
}
413435

414436
.modal-actions button {
415437
border: 0;
416438
background: transparent;
417-
color: #5b636f;
418-
font-size: var(--font-size-body-large);
419-
font-weight: 700;
439+
color: var(--os-primary);
440+
font-size: var(--font-size-body);
441+
font-weight: 500;
442+
padding: 8px 12px;
420443
}
421444

422-
.modal-actions button:last-child {
423-
color: var(--os-primary);
445+
.modal-actions button:disabled {
446+
color: var(--os-grey-500);
424447
}
425448

426449
.tooltip-options {
@@ -435,14 +458,10 @@
435458
margin: 4px 0 0;
436459
}
437460

438-
.outcome-modal {
439-
padding: 2px;
440-
}
441-
442461
.outcome-title {
443462
margin-bottom: 16px;
444463
font-size: 24px;
445-
font-weight: 700;
464+
font-weight: 400;
446465
}
447466

448467
.outcome-radio-list label {

0 commit comments

Comments
 (0)