Files
unsupervised-scheduler/assets/js/blocks.js
T
thatguygriffandClaude Fable 5 7b00811133
CI / Tests (PHP 8.2) (pull_request) Successful in 41s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m45s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m35s
CI / Build Plugin Zip (pull_request) Skipped
Show a sign-in link instead of the form after email confirmation
Closes #67

When a student lands on the registration page from the confirmation
email (?us_confirmed=1), replace the registration form with the
confirmation message and a "Sign in to your account" link — the form
is useless at that point and re-submitting would only produce an
"account already exists" error. A confirmed-but-unapproved student can
already log in (the pending gate only withholds booking), so signing in
is the natural next step.

The link target follows the booking block's pattern: a loginPageId
block attribute (page picker in the editor sidebar) or login_page_id
shortcode attribute, falling back to wp_login_url(). The expired-link
notice keeps the form as before.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-18 17:53:38 -03:00

220 lines
9.3 KiB
JavaScript

/* global wp */
(function () {
'use strict';
const { registerBlockType } = wp.blocks;
const { createElement: el, useState, useEffect } = wp.element;
const { useBlockProps, InspectorControls } = wp.blockEditor;
const { PanelBody, SelectControl, ToggleControl } = wp.components;
const { useSelect } = wp.data;
const apiFetch = wp.apiFetch;
const ServerSideRender = wp.serverSideRender;
const { __ } = wp.i18n;
/**
* Dropdown of published pages with a leading "default" choice.
* Values are page IDs; 0 means the default behaviour.
*/
function PageSelect(props) {
const pages = useSelect(
(select) => select('core').getEntityRecords('postType', 'page', {
per_page: -1,
orderby: 'title',
order: 'asc',
status: 'publish',
_fields: 'id,title',
}),
[]
);
const options = [{ label: props.defaultLabel, value: '0' }].concat(
(pages || []).map((page) => ({
label: (page.title && page.title.rendered) || __('(no title)', 'unsupervised-schedular'),
value: String(page.id),
}))
);
return el(SelectControl, {
label: props.label,
help: props.help,
value: String(props.value || 0),
options: options,
onChange: (value) => props.onChange(parseInt(value, 10) || 0),
});
}
/**
* Dropdown of active group classes fetched from the plugin's public
* offerings endpoint. Values are offering IDs; 0 means all classes.
*/
function GroupClassSelect(props) {
const [offerings, setOfferings] = useState(null);
useEffect(() => {
apiFetch({ path: '/us-scheduler/v1/offerings?kind=group_class' })
.then(setOfferings)
.catch(() => setOfferings([]));
}, []);
const options = [{ label: __('All classes', 'unsupervised-schedular'), value: '0' }].concat(
(offerings || []).map((o) => ({
label: o.title || __('(no title)', 'unsupervised-schedular'),
value: String(o.id),
}))
);
// A previously chosen class that is no longer offered (deleted or
// deactivated) keeps its stored id visible instead of silently
// pretending "All classes" is selected.
const value = String(props.value || 0);
if (offerings !== null && !options.some((opt) => opt.value === value)) {
options.push({
label: __('Unavailable class #', 'unsupervised-schedular') + value,
value: value,
});
}
return el(SelectControl, {
label: props.label,
help: props.help,
value: value,
options: options,
onChange: (newValue) => props.onChange(parseInt(newValue, 10) || 0),
});
}
const blocks = [
{
name: 'us-scheduler/booking',
title: __('Lesson Booking', 'unsupervised-schedular'),
description: __('Lets students browse availability and book lessons. Shows a styled preview in the editor.', 'unsupervised-schedular'),
icon: 'calendar-alt',
keywords: ['booking', 'lesson', 'schedule'],
shortcode: 'us_booking',
attributes: {
loginPageId: { type: 'number', default: 0 },
autoRedirect: { type: 'boolean', default: false },
},
inspector: (attributes, setAttributes) => el(
PanelBody,
{ title: __('Logged-out visitors', 'unsupervised-schedular') },
el(PageSelect, {
label: __('Login page', 'unsupervised-schedular'),
help: __('Where the log-in link sends visitors who are not logged in.', 'unsupervised-schedular'),
defaultLabel: __('WordPress login screen', 'unsupervised-schedular'),
value: attributes.loginPageId,
onChange: (loginPageId) => setAttributes({ loginPageId }),
}),
el(ToggleControl, {
label: __('Redirect automatically', 'unsupervised-schedular'),
help: __('Send logged-out visitors straight to the login page instead of showing a link.', 'unsupervised-schedular'),
checked: !!attributes.autoRedirect,
onChange: (autoRedirect) => setAttributes({ autoRedirect }),
})
),
},
{
name: 'us-scheduler/student-login',
title: __('Student Login', 'unsupervised-schedular'),
description: __('The front-end login form for students.', 'unsupervised-schedular'),
icon: 'admin-users',
keywords: ['login', 'student', 'sign in'],
shortcode: 'us_student_login',
attributes: {
bookingPageId: { type: 'number', default: 0 },
autoRedirect: { type: 'boolean', default: false },
},
inspector: (attributes, setAttributes) => el(
PanelBody,
{ title: __('Logged-in visitors', 'unsupervised-schedular') },
el(PageSelect, {
label: __('Booking page', 'unsupervised-schedular'),
help: __('Where students are sent after logging in, and where the link shown to already-logged-in visitors points.', 'unsupervised-schedular'),
defaultLabel: __('This page', 'unsupervised-schedular'),
value: attributes.bookingPageId,
onChange: (bookingPageId) => setAttributes({ bookingPageId }),
}),
el(ToggleControl, {
label: __('Redirect automatically', 'unsupervised-schedular'),
help: __('Send logged-in visitors straight to the booking page instead of showing a link. Requires a booking page to be chosen.', 'unsupervised-schedular'),
checked: !!attributes.autoRedirect,
onChange: (autoRedirect) => setAttributes({ autoRedirect }),
})
),
},
{
name: 'us-scheduler/student-register',
title: __('Student Registration', 'unsupervised-schedular'),
description: __('The invite-only student registration form.', 'unsupervised-schedular'),
icon: 'welcome-add-page',
keywords: ['register', 'student', 'invite'],
shortcode: 'us_student_register',
attributes: {
loginPageId: { type: 'number', default: 0 },
},
inspector: (attributes, setAttributes) => el(
PanelBody,
{ title: __('After email confirmation', 'unsupervised-schedular') },
el(PageSelect, {
label: __('Sign-in page', 'unsupervised-schedular'),
help: __('Where the sign-in link shown after a student confirms their email address sends them.', 'unsupervised-schedular'),
defaultLabel: __('WordPress login screen', 'unsupervised-schedular'),
value: attributes.loginPageId,
onChange: (loginPageId) => setAttributes({ loginPageId }),
})
),
},
{
name: 'us-scheduler/group-classes',
title: __('Group Classes', 'unsupervised-schedular'),
description: __('Lets students browse and enrol in group classes. Shows a styled preview in the editor.', 'unsupervised-schedular'),
icon: 'groups',
keywords: ['group', 'class', 'enrol'],
shortcode: 'us_group_classes',
attributes: {
offeringId: { type: 'number', default: 0 },
},
inspector: (attributes, setAttributes) => el(
PanelBody,
{ title: __('Classes shown', 'unsupervised-schedular') },
el(GroupClassSelect, {
label: __('Class', 'unsupervised-schedular'),
help: __('Show only one group class, for embedding on a page dedicated to it.', 'unsupervised-schedular'),
value: attributes.offeringId,
onChange: (offeringId) => setAttributes({ offeringId }),
})
),
},
];
blocks.forEach((def) => {
registerBlockType(def.name, {
apiVersion: 3,
title: def.title,
description: def.description,
icon: def.icon,
category: 'widgets',
keywords: def.keywords,
supports: { html: false, multiple: false },
attributes: def.attributes || {},
example: {},
edit: function Edit(props) {
const inspector = def.inspector
? el(InspectorControls, {}, def.inspector(props.attributes, props.setAttributes))
: null;
return el(
'div',
useBlockProps(),
inspector,
el(ServerSideRender, { block: def.name, attributes: props.attributes })
);
},
save: () => null,
transforms: {
from: [{ type: 'shortcode', tag: def.shortcode }],
},
});
});
}());