diff --git a/.github/hooks/commit-msg.bash b/.github/hooks/commit-msg.bash index 8719d116d25..332e66d2219 100755 --- a/.github/hooks/commit-msg.bash +++ b/.github/hooks/commit-msg.bash @@ -38,7 +38,7 @@ fi if [ ! -z "$third_line" ]; then if [[ "$third_line" =~ ^(refs|ref:) ]]; then echo -e "${red}Error: Third line should not start with 'refs' or 'ref:'${no_color}" >&2 - echo -e "Use 'ref ', 'fixes ', or 'closes ' instead" >&2 + echo -e "Use a supported keyword like 'ref', 'close', 'fixes', 'related to', or 'contributes to' followed by an issue link (or 'no ref')" >&2 echo -e "${yellow}Press Enter to edit the message...${no_color}" >&2 read < /dev/tty # Wait for Enter key press from the terminal @@ -67,8 +67,9 @@ if [ ! -z "$third_line" ]; then # If fixed, the script will continue to the next checks fi - if ! [[ "$third_line" =~ ^(ref|fixes|closes)\ .*$ ]]; then - echo -e "${yellow}Warning: Third line should start with 'ref', 'fixes', or 'closes' followed by an issue link${no_color}" >&2 + if ! [[ "$third_line" =~ ^(close|closes|closed|closing|fix|fixes|fixed|fixing|resolve|resolves|resolved|resolving|complete|completes|completed|completing|ref|references|part\ of|related\ to|contributes\ to|towards)\ .*$ ]] \ + && ! [[ "$third_line" == "no ref" ]]; then + echo -e "${yellow}Warning: Third line should start with a supported issue-link keyword (for example: 'ref', 'close', 'fixes', 'related to', or 'contributes to') or be 'no ref'${no_color}" >&2 fi fi diff --git a/apps/admin-x-settings/src/components/settings/membership/access.tsx b/apps/admin-x-settings/src/components/settings/membership/access.tsx index 111ecf8c434..50753961d4a 100644 --- a/apps/admin-x-settings/src/components/settings/membership/access.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/access.tsx @@ -168,15 +168,17 @@ const Access: React.FC<{ keywords: string[] }> = ({keywords}) => { const form = ( {isTrialMode && ( - -
-
Pre-launch mode
-
- During your free trial, a private access code is required to browse your site. When you're ready to launch, pick a plan to upgrade your account and make everything public. +
+ +
+
Pre-launch mode
+
+ During your free trial, a private access code is required to browse your site. When you're ready to launch, pick a plan to upgrade your account and make everything public. +
-
- Upgrade now - + Upgrade now + +
)}
Who should be able to browse your site?
@@ -310,7 +312,6 @@ const Access: React.FC<{ keywords: string[] }> = ({keywords}) => { keywords={keywords} navid='members' saveState={saveState} - styles={isTrialMode ? 'overflow-hidden' : undefined} testId='access' title='Access' hideEditButton diff --git a/apps/admin-x-settings/test/acceptance/membership/access.test.ts b/apps/admin-x-settings/test/acceptance/membership/access.test.ts index 8495701e8b2..eed72a79da6 100644 --- a/apps/admin-x-settings/test/acceptance/membership/access.test.ts +++ b/apps/admin-x-settings/test/acceptance/membership/access.test.ts @@ -154,6 +154,39 @@ test.describe('Access settings', async () => { await expect(accessCode).toHaveValue('fake-456'); }); + test('Does not clip dropdown options off the Access card in pre-launch mode', async ({page}) => { + await mockApi({page, requests: { + ...globalDataRequests, + browseConfig: createConfigWithLimits({ + publicSiteAccess: { + disabled: true, + error: 'This plan does not include public site access' + } + }) + }}); + + await page.goto('/'); + + const section = page.getByTestId('access'); + + await expect(section.getByText('Pre-launch mode')).toBeVisible(); + + await section.getByTestId('commenting-select').click(); + + const lastOption = page.locator('[data-testid="select-option"]', {hasText: 'Nobody'}); + await expect(lastOption).toBeVisible(); + + const box = await lastOption.boundingBox(); + expect(box).not.toBeNull(); + + const optionIsActuallyPainted = await page.evaluate(({x, y, width, height}) => { + const el = document.elementFromPoint(x + width / 2, y + height / 2); + return Boolean(el?.closest('[data-testid="select-option"]')); + }, box!); + + expect(optionIsActuallyPainted).toBe(true); + }); + test('Disables other sections when signup is disabled', async ({page}) => { const {lastApiRequests} = await mockApi({page, requests: { ...globalDataRequests, diff --git a/apps/admin/index.html b/apps/admin/index.html index d03dd543ef6..63d1f021ddd 100644 --- a/apps/admin/index.html +++ b/apps/admin/index.html @@ -14,8 +14,8 @@ - - + +
diff --git a/apps/admin/public/assets/img/apple-touch-icon.png b/apps/admin/src/assets/img/apple-touch-icon.png similarity index 100% rename from apps/admin/public/assets/img/apple-touch-icon.png rename to apps/admin/src/assets/img/apple-touch-icon.png diff --git a/apps/admin/public/assets/img/favicon.ico b/apps/admin/src/assets/img/favicon.ico similarity index 100% rename from apps/admin/public/assets/img/favicon.ico rename to apps/admin/src/assets/img/favicon.ico diff --git a/apps/posts/src/views/Automations/editor.tsx b/apps/posts/src/views/Automations/editor.tsx index 6129581991a..bec6da2db06 100644 --- a/apps/posts/src/views/Automations/editor.tsx +++ b/apps/posts/src/views/Automations/editor.tsx @@ -39,25 +39,7 @@ const editableSlice = (automation: AutomationDetail) => ({ }); const isFailedEditState = (editState: AutomationEditState): boolean => { - switch (editState) { - case 'failed to publish': - case 'failed to re-publish': - case 'failed to save': - case 'failed to unpublish': - return true; - case 'confirming re-publish': - case 'confirming unpublish': - case 'idle': - case 'publishing': - case 're-publishing': - case 'saving': - case 'unpublishing': - return false; - default: { - const _exhaustive: never = editState; - throw new Error(`Unhandled edit state: ${_exhaustive}`); - } - } + return editState.phase === 'failed'; }; const getActionErrors = (automation: AutomationDetail): Record => { @@ -92,7 +74,7 @@ const AutomationEditor: React.FC = () => { const automation = data?.automations[0]; const editMutation = useEditAutomation(); - const [editState, setEditState] = React.useState('idle'); + const [editState, setEditState] = React.useState({phase: 'idle'}); const [actionErrors, setActionErrors] = React.useState>({}); // Draft is the user-facing, locally mutable copy. The React Query cache stays as server truth; @@ -126,7 +108,7 @@ const AutomationEditor: React.FC = () => { ); }); setEditState(prev => ( - isFailedEditState(prev) ? 'idle' : prev + isFailedEditState(prev) ? {phase: 'idle'} : prev )); }; @@ -157,20 +139,20 @@ const AutomationEditor: React.FC = () => { const statusTransition: `${AutomationStatus} -> ${AutomationStatus}` = `${oldStatus} -> ${newStatus}`; switch (statusTransition) { case 'active -> active': - requestState = 're-publishing'; - errorState = 'failed to re-publish'; + requestState = {phase: 'submitting', action: 'republish'}; + errorState = {phase: 'failed', action: 'republish'}; break; case 'inactive -> inactive': - requestState = 'saving'; - errorState = 'failed to save'; + requestState = {phase: 'submitting', action: 'save'}; + errorState = {phase: 'failed', action: 'save'}; break; case 'inactive -> active': - requestState = 'publishing'; - errorState = 'failed to publish'; + requestState = {phase: 'submitting', action: 'publish'}; + errorState = {phase: 'failed', action: 'publish'}; break; case 'active -> inactive': - requestState = 'unpublishing'; - errorState = 'failed to unpublish'; + requestState = {phase: 'submitting', action: 'unpublish'}; + errorState = {phase: 'failed', action: 'unpublish'}; break; default: { const _exhaustive: never = statusTransition; @@ -195,7 +177,7 @@ const AutomationEditor: React.FC = () => { onSuccess: (response) => { setDraft(response.automations[0]); setActionErrors({}); - setEditState('idle'); + setEditState({phase: 'idle'}); }, onError: (error) => { void error; @@ -218,9 +200,9 @@ const AutomationEditor: React.FC = () => { ); }; - let isConfirmUnpublishAlertOpen = false; - let isConfirmRepublishAlertOpen = false; - let isEditRequestActive = false; + const isConfirmUnpublishAlertOpen = editState.action === 'unpublish'; + const isConfirmRepublishAlertOpen = editState.action === 'republish'; + const isEditRequestActive = editState.phase === 'submitting'; let isSaveButtonEnabled = !!draft && draft.actions.length > 0 && draft.status === 'inactive' && hasUnsavedChanges; let saveButtonVariant: ButtonProps['variant'] = 'secondary'; let saveButtonChildren: React.ReactNode = 'Save'; @@ -234,89 +216,84 @@ const AutomationEditor: React.FC = () => { let isRepublishButtonEnabled = true; let republishButtonVariant: ButtonProps['variant'] = 'default'; let republishButtonChildren: React.ReactNode = 'Publish changes'; - switch (editState) { + switch (editState.phase) { case 'idle': break; - case 'saving': - isEditRequestActive = true; - isSaveButtonEnabled = false; - isPublishButtonEnabled = false; - isTurnOffButtonEnabled = false; - saveButtonChildren = ( - <> - - Saving... - - ); - break; - case 'publishing': - isEditRequestActive = true; - isSaveButtonEnabled = false; - isPublishButtonEnabled = false; - isTurnOffButtonEnabled = false; - publishButtonChildren = ( - <> - - Publishing... - - ); - break; - case 're-publishing': - isEditRequestActive = true; - isConfirmRepublishAlertOpen = true; - isPublishButtonEnabled = false; - isTurnOffButtonEnabled = false; - isRepublishButtonEnabled = false; - republishButtonChildren = ( - <> - - Publishing... - - ); - break; - case 'unpublishing': - isEditRequestActive = true; - isConfirmUnpublishAlertOpen = true; - isSaveButtonEnabled = false; - isPublishButtonEnabled = false; - isTurnOffButtonEnabled = false; - turnOffButtonChildren = ( - <> - - Turning off... - - ); - break; - case 'confirming unpublish': - isConfirmUnpublishAlertOpen = true; + case 'submitting': isSaveButtonEnabled = false; isPublishButtonEnabled = false; isTurnOffButtonEnabled = false; + + switch (editState.action) { + case 'save': + saveButtonChildren = ( + <> + + Saving... + + ); + break; + case 'publish': + publishButtonChildren = ( + <> + + Publishing... + + ); + break; + case 'republish': + isRepublishButtonEnabled = false; + republishButtonChildren = ( + <> + + Publishing... + + ); + break; + case 'unpublish': + turnOffButtonChildren = ( + <> + + Turning off... + + ); + break; + } break; - case 'confirming re-publish': - isConfirmRepublishAlertOpen = true; - isPublishButtonEnabled = false; - isTurnOffButtonEnabled = false; - break; - case 'failed to save': - saveButtonVariant = 'destructive'; - saveButtonChildren = 'Retry'; - break; - case 'failed to publish': - publishButtonVariant = 'destructive'; - publishButtonChildren = 'Retry'; - break; - case 'failed to re-publish': - isConfirmRepublishAlertOpen = true; - isPublishButtonEnabled = false; - isTurnOffButtonEnabled = false; - republishButtonVariant = 'destructive'; - republishButtonChildren = 'Retry'; + case 'confirming': + switch (editState.action) { + case 'republish': + isPublishButtonEnabled = false; + isTurnOffButtonEnabled = false; + break; + case 'unpublish': + isSaveButtonEnabled = false; + isPublishButtonEnabled = false; + isTurnOffButtonEnabled = false; + break; + } break; - case 'failed to unpublish': - isConfirmUnpublishAlertOpen = true; - isTurnOffButtonEnabled = true; - turnOffButtonChildren = 'Retry'; + case 'failed': + switch (editState.action) { + case 'save': + saveButtonVariant = 'destructive'; + saveButtonChildren = 'Retry'; + break; + case 'publish': + publishButtonVariant = 'destructive'; + publishButtonChildren = 'Retry'; + break; + case 'republish': + isPublishButtonEnabled = false; + isTurnOffButtonEnabled = false; + republishButtonVariant = 'destructive'; + republishButtonChildren = 'Retry'; + break; + case 'unpublish': + isTurnOffButtonEnabled = true; + turnOffButtonChildren = 'Retry'; + break; + } break; default: { const _exhaustive: never = editState; @@ -326,12 +303,12 @@ const AutomationEditor: React.FC = () => { const onConfirmUnpublishOpenChange = (open: boolean): void => { setEditState((oldEditState) => { - switch (oldEditState) { - case 'confirming unpublish': - case 'failed to unpublish': - return open ? oldEditState : 'idle'; + switch (oldEditState.phase) { + case 'confirming': + case 'failed': + return oldEditState.action === 'unpublish' && !open ? {phase: 'idle'} : oldEditState; case 'idle': - return open ? 'confirming unpublish' : oldEditState; + return open ? {phase: 'confirming', action: 'unpublish'} : oldEditState; default: return oldEditState; } @@ -340,12 +317,12 @@ const AutomationEditor: React.FC = () => { const onConfirmRepublishOpenChange = (open: boolean): void => { setEditState((oldEditState) => { - switch (oldEditState) { - case 'confirming re-publish': - case 'failed to re-publish': - return open ? oldEditState : 'idle'; + switch (oldEditState.phase) { + case 'confirming': + case 'failed': + return oldEditState.action === 'republish' && !open ? {phase: 'idle'} : oldEditState; case 'idle': - return open ? 'confirming re-publish' : oldEditState; + return open ? {phase: 'confirming', action: 'republish'} : oldEditState; default: return oldEditState; } @@ -359,10 +336,10 @@ const AutomationEditor: React.FC = () => { switch (draft.status) { case 'active': - if (!validateActionErrors(draft, 'idle')) { + if (!validateActionErrors(draft, {phase: 'idle'})) { return; } - setEditState('confirming re-publish'); + setEditState({phase: 'confirming', action: 'republish'}); break; case 'inactive': save('active'); @@ -399,7 +376,7 @@ const AutomationEditor: React.FC = () => { saveButtonVariant={saveButtonVariant} onPublish={onPublish} onSave={() => save()} - onTurnOff={() => setEditState('confirming unpublish')} + onTurnOff={() => setEditState({phase: 'confirming', action: 'unpublish'})} /> { Cancel