Skip to content

Commit 067961e

Browse files
author
Herve Tribouilloy
committed
Simplified event state and improved login capability
1 parent ca2477c commit 067961e

11 files changed

Lines changed: 141 additions & 110 deletions

File tree

vite_project/src/components/BookingDrawer.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ export const BookingDrawer: React.FC<BookingDrawerProps> = ({
2121
<h3>Set your appointment</h3>
2222
<button className="drawer-close"
2323
aria-label="Close"
24-
onClick={onClose
25-
}>×</button>
24+
onClick={onClose}>×</button>
2625
</div>
2726
{children}
2827
</div>

vite_project/src/components/event/Dashboard/DayEvent/AddToCart.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export function AddToCart({onRequireAuth}: AddToCartProps) {
2525
const [verifiedAt, setVerifiedAt] = useState<number | null>(null);
2626
const [awaitingSecurity, setAwaitingSecurity] = useState(false);
2727
const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
28+
const [token, setToken] = useState<string | null>(null);
2829

2930
const isHumanVerified =
3031
turnstileToken &&
@@ -140,6 +141,7 @@ export function AddToCart({onRequireAuth}: AddToCartProps) {
140141
<Turnstile
141142
siteKey={cloudflareKey}
142143
containerId="booking-turnstile"
144+
onToken={setToken}
143145
/>
144146
)}
145147
<UserState />

vite_project/src/components/event/Dashboard/DayEvent/DayEventGroup.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {SetEventDetail} from "./SetEventDetail.tsx";
22
import type {DayGroupEvent} from "../../../../types/domain/dashboard.type.tsx";
3-
import {EventStateProvider} from "../../../../state/Event/EventStateProvider.tsx";
43
import {getTime} from "../../../../lib/date.ts";
54
import {useDashboardState} from "../../../../state/Dashboard/useDashboardState.ts";
65
import {EventHostView} from "./EventHostView.tsx";
@@ -24,7 +23,6 @@ export const DayEventGroup: React.FC<ListingProps> = ({ eventGroup, onView }) =>
2423
}
2524

2625
return (
27-
<EventStateProvider eventGroup={eventGroup}>
2826
<div className={`event-card${highlight() ? "--highlighting" : ""}`}>
2927
<div className="card-info-hidden">
3028
{eventGroup.startTime}
@@ -47,13 +45,10 @@ export const DayEventGroup: React.FC<ListingProps> = ({ eventGroup, onView }) =>
4745
}}
4846
>
4947
{eventGroup.eventIds && (
50-
<EventStateProvider eventGroup={eventGroup}>
51-
<DrawerContent eventIds={eventGroup.eventIds}/>
52-
</EventStateProvider>
48+
<DrawerContent eventIds={eventGroup.eventIds}/>
5349
)}
5450
</BookingDrawer>
5551
)}
5652
</div>
57-
</EventStateProvider>
5853
);
5954
};

vite_project/src/components/event/Dashboard/GetWeekEvents.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {useDashboardState} from "../../../state/Dashboard/useDashboardState.ts";
1010
import type {Venue} from "../../../types/domain/types.ts";
1111
import {activity} from "../../../../activity";
1212
import {GroupEventStateProvider} from "../../../state/GroupEvent/GroupEventStateProvider.tsx";
13+
import {EventStateProvider} from "../../../state/Event/EventStateProvider.tsx";
1314

1415
export function GetWeekEvents() {
1516
const { visitIntent } = useVisitIntentState();
@@ -58,6 +59,8 @@ export function GetWeekEvents() {
5859
}
5960

6061
return <GroupEventStateProvider>
61-
<WeekEvents events={events} />
62+
<EventStateProvider>
63+
<WeekEvents events={events} />
64+
</EventStateProvider>
6265
</GroupEventStateProvider>;
6366
}

vite_project/src/components/event/Dashboard/WeekEvents.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {useUserState} from "../../../state/User/useUserState.ts";
77
import {useMediaQuery} from "../../../hooks/ui/useMediaQuery.tsx";
88
import type {DayGroupEvent} from "../../../types/domain/dashboard.type.tsx";
99
import {useGroupEventState} from "../../../state/GroupEvent/useGroupEventState.ts";
10+
import {useEventState} from "../../../state/Event/useEventState.ts";
11+
import {useEffect, useRef} from "react";
1012

1113
interface WeekEventProps {
1214
events: IntentEvent[];
@@ -16,6 +18,18 @@ export function WeekEvents({ events }: WeekEventProps) {
1618
const { user } = useUserState();
1719
const isMobile = useMediaQuery('(max-width: 768px)');
1820
const { toggleActiveGroupEvent } = useGroupEventState();
21+
const { showBooking } = useEventState()
22+
23+
const prevUserRef = useRef<typeof user>(null);
24+
25+
useEffect(() => {
26+
// Auth boundary: unauthenticated → authenticated
27+
if (!prevUserRef.current && user) {
28+
showBooking();
29+
}
30+
31+
prevUserRef.current = user;
32+
}, [user, showBooking]);
1933

2034
return (
2135
<div
@@ -40,6 +54,7 @@ export function WeekEvents({ events }: WeekEventProps) {
4054
eventGroup={eventGroup}
4155
onView={(eventIds) => {
4256
toggleActiveGroupEvent(eventIds);
57+
showBooking()
4358
}}
4459
/>
4560
)

vite_project/src/components/event/Dashboard/WeekEvents/DrawerContent.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export const DrawerContent: React.FC<ViewGroupEventProps> = ({ eventIds }) => {
4040
<SignUp onSuccess={showBooking} onCancel={showSignIn} />
4141
}
4242
</div>
43-
4443
{eventState.drawerContent === 'booking' && (<div className="drawer-actions">
4544
<AddToCart onRequireAuth={() => {
4645
showSignIn()}

vite_project/src/components/user-authentication/Sign.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const Sign: React.FC = () => {
2525
};
2626

2727
return (
28-
<form className="drawer-auth-form" onSubmit={handleSubmit}>
28+
<form className="widget-form" onSubmit={handleSubmit}>
2929
<h3>Sign in to confirm your appointment</h3>
3030

3131
{errorMessage && <p className="form-error">{errorMessage}</p>}
Lines changed: 92 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import {useState} from "react";
22
import {useRegisterUser} from "../../hooks/domain/useRegisterUser.tsx";
33
import {Spinner} from "../global/Spinner.tsx";
4+
import {useSystemState} from "../../state/System/useSystemState.ts";
5+
import {activity} from "../../../activity";
6+
import {Turnstile} from "../../security/Turnstile.tsx";
47

58
interface SignUpProps {
69
onSuccess?: () => void;
@@ -14,11 +17,18 @@ export const SignUp: React.FC<SignUpProps> = ({ onSuccess, onCancel }) => {
1417
password: '',
1518
confirmPassword: '',
1619
});
20+
const [token, setToken] = useState<string | null>(null);
1721
const { register, loadingRegister } = useRegisterUser();
22+
const { cloudflareKey, isTurnstileEnabled } = useSystemState()
1823

1924
const [error, setError] = useState<string | null>(null);
2025
const [loading, setLoading] = useState(false);
2126

27+
const turnstileEnabled = isTurnstileEnabled();
28+
const canSubmit =
29+
status !== "loading" &&
30+
(!turnstileEnabled || Boolean(token));
31+
2232
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
2333
setValues(v => ({ ...v, [e.target.name]: e.target.value }));
2434
}
@@ -55,73 +65,91 @@ export const SignUp: React.FC<SignUpProps> = ({ onSuccess, onCancel }) => {
5565
}
5666
}
5767

68+
if (!turnstileEnabled) {
69+
activity('form-ready', 'Turnstile Disabled',{
70+
cloudflareKey
71+
}, 'warn');
72+
}
73+
5874
if (loadingRegister) return <Spinner />
5975

6076
return (
61-
<form className="widget-form" onSubmit={handleSubmit}>
62-
<h2>Sign Up</h2>
63-
64-
{error && <p className="form-error">{error}</p>}
65-
66-
<fieldset disabled={loading}>
67-
<label>
68-
Name
69-
<input
70-
name="name"
71-
required
72-
value={values.name}
73-
onChange={handleChange}
74-
autoComplete="name"
75-
/>
76-
</label>
77-
78-
<label>
79-
Email
80-
<input
81-
name="email"
82-
type="email"
83-
required
84-
value={values.email}
85-
onChange={handleChange}
86-
autoComplete="email"
87-
/>
88-
</label>
89-
90-
<label>
91-
Password
92-
<input
93-
name="password"
94-
type="password"
95-
required
96-
minLength={6}
97-
value={values.password}
98-
onChange={handleChange}
99-
autoComplete="new-password"
100-
/>
101-
</label>
102-
103-
<label>
104-
Confirm Password
105-
<input
106-
name="confirmPassword"
107-
type="password"
108-
required
109-
value={values.confirmPassword}
110-
onChange={handleChange}
111-
autoComplete="new-password"
112-
/>
113-
</label>
114-
115-
<button type="submit">
116-
{loading ? 'Signing up…' : 'Sign Up'}
117-
</button>
118-
119-
{onCancel && (
120-
<button type="button" onClick={onCancel}>
121-
Already have an account? Sign in
77+
<>
78+
<form className="widget-form" onSubmit={handleSubmit}>
79+
<h2>Sign Up</h2>
80+
81+
{error && <p className="form-error">{error}</p>}
82+
83+
<fieldset disabled={loading}>
84+
<label>
85+
Name
86+
<input
87+
name="name"
88+
required
89+
value={values.name}
90+
onChange={handleChange}
91+
autoComplete="name"
92+
/>
93+
</label>
94+
95+
<label>
96+
Email
97+
<input
98+
name="email"
99+
type="email"
100+
required
101+
value={values.email}
102+
onChange={handleChange}
103+
autoComplete="email"
104+
/>
105+
</label>
106+
107+
<label>
108+
Password
109+
<input
110+
name="password"
111+
type="password"
112+
required
113+
minLength={6}
114+
value={values.password}
115+
onChange={handleChange}
116+
autoComplete="new-password"
117+
/>
118+
</label>
119+
120+
<label>
121+
Confirm Password
122+
<input
123+
name="confirmPassword"
124+
type="password"
125+
required
126+
value={values.confirmPassword}
127+
onChange={handleChange}
128+
autoComplete="new-password"
129+
/>
130+
</label>
131+
132+
<button type="submit"
133+
disabled={!canSubmit}
134+
>
135+
{loading ? 'Signing up…' : 'Sign Up'}
122136
</button>
123-
)}
124-
</fieldset>
125-
</form>
137+
138+
{onCancel && (
139+
<button type="button" onClick={onCancel}>
140+
Already have an account? Sign in
141+
</button>
142+
)}
143+
</fieldset>
144+
</form>
145+
{/* 2. The security gate */}
146+
{turnstileEnabled && (
147+
<Turnstile
148+
siteKey={cloudflareKey}
149+
containerId="booking-turnstile"
150+
onToken={setToken}
151+
/>
152+
)}
153+
</>
126154
);
127155
};

vite_project/src/components/user-authentication/SignWithGoogle.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ export const SignWithGoogle: React.FC = () => {
99
};
1010

1111
return (
12-
<form className="drawer-auth-form" >
12+
<div className="drawer-auth-form" >
1313
<button onClick={handleLogin}>Continue with Google</button>
14-
</form>
14+
</div>
1515
)
1616
};
Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { useEffect, useRef } from "react";
2-
import { ensureTurnstileLoaded } from "./turnstileService.ts";
2+
import {ensureTurnstileLoaded} from "./turnstileService.ts";
33
import {activity} from "../../activity";
44

5+
56
type TurnstileProps = {
67
siteKey: string;
7-
containerId: string;
8+
onToken: (token: string | null) => void;
9+
containerId: string; // 👈 REQUIRED
810
};
911

10-
export function Turnstile({ siteKey, containerId }: TurnstileProps) {
12+
export function Turnstile({ siteKey, onToken, containerId }: TurnstileProps) {
1113
const widgetId = useRef<string | null>(null);
1214

1315
useEffect(() => {
@@ -17,29 +19,19 @@ export function Turnstile({ siteKey, containerId }: TurnstileProps) {
1719
if (cancelled || !window.turnstile) return;
1820

1921
const container = document.getElementById(containerId);
22+
2023
if (!container) {
21-
activity('turnstile', `[turnstile] container #${containerId} not found`, null, 'error');
24+
activity('turnstile-load', `[turnstile] container #${containerId} not found`,{
25+
containerId,
26+
siteKey
27+
});
2228
return;
2329
}
2430

25-
activity('turnstile', '[turnstile] container rendering', null, 'info');
26-
2731
widgetId.current = window.turnstile.render(container, {
2832
sitekey: siteKey,
29-
callback: (token: string) => {
30-
activity('turnstile', '[turnstile] event sent', {"eventName": "booking:security-success"}, 'info');
31-
window.dispatchEvent(
32-
new CustomEvent("booking:security-success", {
33-
detail: { token }
34-
})
35-
);
36-
},
37-
"expired-callback": () => {
38-
activity('turnstile', '[turnstile] event sent', {"eventName": "booking:security-expired"}, 'info');
39-
window.dispatchEvent(
40-
new CustomEvent("booking:security-expired")
41-
);
42-
}
33+
callback: onToken,
34+
"expired-callback": () => onToken(null),
4335
});
4436
});
4537

@@ -52,4 +44,4 @@ export function Turnstile({ siteKey, containerId }: TurnstileProps) {
5244
}, [siteKey, containerId]);
5345

5446
return null;
55-
}
47+
}

0 commit comments

Comments
 (0)