2025-02-21 16:02:48 +00:00
|
|
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
2025-06-13 14:17:35 +00:00
|
|
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
2025-02-21 16:02:48 +00:00
|
|
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
2025-08-06 10:09:15 +00:00
|
|
|
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
2025-02-21 16:02:48 +00:00
|
|
|
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
2025-07-03 12:42:10 +00:00
|
|
|
import { ApolloError } from '@apollo/client';
|
2025-02-21 16:02:48 +00:00
|
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
|
|
import { Trans, useLingui } from '@lingui/react/macro';
|
2025-06-13 14:17:35 +00:00
|
|
|
import { Controller, useForm } from 'react-hook-form';
|
🔧 Restore PRs #14348 and #14352 that were reverted by PR #14347 (#14359)
## Problem
**CRITICAL:** Two PRs were accidentally reverted when PR #14347 "Prevent
csv export injections" was merged:
1. **PR #14348** "[Page Layout] - Review Refactor" - ✅ **RESTORED**
2. **PR #14352** "Fix wrong path used by backend" - ✅ **RESTORED**
## Root Cause Analysis
During the merge of PR #14347, there was a complex merge conflict with
PR #14352 "Fix wrong path used by backend". The merge commit
`324d7204bb` in the PR #14347 branch brought in changes from PR #14352,
but during the conflict resolution, **BOTH PR #14348 and PR #14352's
changes were accidentally overwritten**.
## What This PR Restores
This PR restores **BOTH** PRs by cherry-picking their commits:
### ✅ PR #14348 Changes Restored:
- `GraphWidgetRenderer.tsx` - was deleted, now restored
- `WidgetRenderer.tsx` - was missing, now restored
- `SettingsPageLayoutTabsInstanceId.ts` - was deleted, now restored
- `useUpdatePageLayoutWidget.ts` - was renamed back, now restored with
correct name
- Multiple test files that were deleted
- Several hook files that were renamed/reverted
- File renames: `usePageLayoutWidgetUpdate.ts` →
`useUpdatePageLayoutWidget.ts`
- Hook refactoring and test file organization
- Page layout component improvements
### ✅ PR #14352 Changes Restored:
- **Types moved to twenty-shared:**
- `packages/twenty-shared/src/types/AppBasePath.ts` ✅ RESTORED
- `packages/twenty-shared/src/types/AppPath.ts` ✅ RESTORED
- `packages/twenty-shared/src/types/SettingsPath.ts` ✅ RESTORED
- **Navigation utilities moved to twenty-shared:**
- `packages/twenty-shared/src/utils/navigation/getAppPath.ts` ✅ RESTORED
- `packages/twenty-shared/src/utils/navigation/getSettingsPath.ts` ✅
RESTORED
- **200+ import statements updated** across the codebase to use
twenty-shared
- **Old type files deleted** from twenty-front/src/modules/types/
## Evidence of Complete Restoration
**Before (reverted state):**
- ❌ Types were in `packages/twenty-front/src/modules/types/`
- ❌ Page layout files missing
- ❌ Hook files incorrectly named
**After (this PR):**
- ✅ Types correctly in `packages/twenty-shared/src/types/`
- ✅ All page layout files restored
- ✅ Hook files correctly named
- ✅ All import statements updated
## Verification
**Total changes:**
- PR #14348: 36 files changed, 863 insertions(+), 442 deletions(-)
- PR #14352: 243 files changed, 492 insertions(+), 461 deletions(-)
- **Combined: 279 files changed, 1355 insertions(+), 903 deletions(-)**
## Impact
This completely restores both PRs that were accidentally lost, ensuring:
1. Page layout refactoring work is back
2. Type organization and path utilities are correctly in twenty-shared
3. Backend email paths work correctly again
4. No functionality is lost
Fixes the reversion caused by the merge conflict in PR #14347.
---------
Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
2025-09-08 19:48:13 +00:00
|
|
|
import { SettingsPath } from 'twenty-shared/types';
|
|
|
|
|
import { getSettingsPath } from 'twenty-shared/utils';
|
2025-04-03 09:47:55 +00:00
|
|
|
import { H2Title } from 'twenty-ui/display';
|
|
|
|
|
import { Section } from 'twenty-ui/layout';
|
2025-06-13 14:17:35 +00:00
|
|
|
import { z } from 'zod';
|
2025-07-01 16:29:32 +00:00
|
|
|
import { useCreateApprovedAccessDomainMutation } from '~/generated-metadata/graphql';
|
2025-06-13 14:17:35 +00:00
|
|
|
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
2025-02-21 16:02:48 +00:00
|
|
|
|
|
|
|
|
export const SettingsSecurityApprovedAccessDomain = () => {
|
|
|
|
|
const navigate = useNavigateSettings();
|
|
|
|
|
|
|
|
|
|
const { t } = useLingui();
|
|
|
|
|
|
2025-07-03 12:42:10 +00:00
|
|
|
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
2025-02-21 16:02:48 +00:00
|
|
|
|
|
|
|
|
const [createApprovedAccessDomain] = useCreateApprovedAccessDomainMutation();
|
|
|
|
|
|
2025-02-25 10:37:36 +00:00
|
|
|
const form = useForm<{ domain: string; email: string }>({
|
2025-02-21 16:02:48 +00:00
|
|
|
mode: 'onSubmit',
|
|
|
|
|
resolver: zodResolver(
|
2025-09-24 16:29:05 +00:00
|
|
|
z.strictObject({
|
|
|
|
|
domain: z
|
|
|
|
|
.string()
|
|
|
|
|
.regex(
|
|
|
|
|
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])\.[a-zA-Z]{2,}$/,
|
|
|
|
|
{
|
|
|
|
|
message: t`Domains have to be smaller than 256 characters, cannot contain spaces and cannot contain any special characters.`,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.max(256),
|
|
|
|
|
email: z.string().min(1, {
|
|
|
|
|
message: t`Email cannot be empty`,
|
|
|
|
|
}),
|
|
|
|
|
}),
|
2025-02-21 16:02:48 +00:00
|
|
|
),
|
|
|
|
|
defaultValues: {
|
|
|
|
|
email: '',
|
|
|
|
|
domain: '',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-25 10:37:36 +00:00
|
|
|
const domain = form.watch('domain');
|
2025-02-21 16:02:48 +00:00
|
|
|
|
|
|
|
|
const handleSave = async () => {
|
|
|
|
|
try {
|
|
|
|
|
createApprovedAccessDomain({
|
|
|
|
|
variables: {
|
|
|
|
|
input: {
|
2025-02-25 10:37:36 +00:00
|
|
|
domain: form.getValues('domain'),
|
|
|
|
|
email: form.getValues('email') + '@' + form.getValues('domain'),
|
2025-02-21 16:02:48 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
onCompleted: () => {
|
2025-07-03 12:42:10 +00:00
|
|
|
enqueueSuccessSnackBar({
|
|
|
|
|
message: t`Please check your email for a verification link.`,
|
2025-02-21 16:02:48 +00:00
|
|
|
});
|
2025-08-30 14:43:04 +00:00
|
|
|
navigate(SettingsPath.Domains);
|
2025-02-21 16:02:48 +00:00
|
|
|
},
|
|
|
|
|
onError: (error) => {
|
2025-07-03 12:42:10 +00:00
|
|
|
enqueueErrorSnackBar({
|
|
|
|
|
apolloError: error instanceof ApolloError ? error : undefined,
|
2025-02-21 16:02:48 +00:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2025-07-03 12:42:10 +00:00
|
|
|
enqueueErrorSnackBar({
|
|
|
|
|
apolloError: error instanceof ApolloError ? error : undefined,
|
2025-02-21 16:02:48 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
2025-02-25 10:37:36 +00:00
|
|
|
<form onSubmit={form.handleSubmit(handleSave)}>
|
|
|
|
|
<SubMenuTopBarContainer
|
|
|
|
|
title="New Approved Access Domain"
|
|
|
|
|
actionButton={
|
|
|
|
|
<SaveAndCancelButtons
|
2025-08-30 14:43:04 +00:00
|
|
|
onCancel={() => navigate(SettingsPath.Domains)}
|
2025-02-25 10:37:36 +00:00
|
|
|
isSaveDisabled={form.formState.isSubmitting}
|
2025-02-21 16:02:48 +00:00
|
|
|
/>
|
2025-02-25 10:37:36 +00:00
|
|
|
}
|
|
|
|
|
links={[
|
|
|
|
|
{
|
|
|
|
|
children: <Trans>Workspace</Trans>,
|
|
|
|
|
href: getSettingsPath(SettingsPath.Workspace),
|
|
|
|
|
},
|
|
|
|
|
{
|
2025-08-30 14:43:04 +00:00
|
|
|
children: <Trans>Domains</Trans>,
|
|
|
|
|
href: getSettingsPath(SettingsPath.Domains),
|
2025-02-25 10:37:36 +00:00
|
|
|
},
|
|
|
|
|
{ children: <Trans>New Approved Access Domain</Trans> },
|
|
|
|
|
]}
|
|
|
|
|
>
|
|
|
|
|
<SettingsPageContainer>
|
|
|
|
|
<Section>
|
|
|
|
|
<H2Title
|
|
|
|
|
title={t`Domain`}
|
|
|
|
|
description={t`The name of your Domain`}
|
|
|
|
|
/>
|
|
|
|
|
<Controller
|
|
|
|
|
name="domain"
|
|
|
|
|
control={form.control}
|
|
|
|
|
render={({
|
|
|
|
|
field: { onChange, value },
|
|
|
|
|
fieldState: { error },
|
|
|
|
|
}) => (
|
2025-08-06 10:09:15 +00:00
|
|
|
<SettingsTextInput
|
2025-07-07 13:42:12 +00:00
|
|
|
instanceId="approved-access-domain"
|
2025-05-23 09:03:31 +00:00
|
|
|
autoFocus
|
2025-02-25 10:37:36 +00:00
|
|
|
autoComplete="off"
|
|
|
|
|
value={value}
|
2025-05-23 09:03:31 +00:00
|
|
|
onChange={onChange}
|
2025-02-25 10:37:36 +00:00
|
|
|
fullWidth
|
|
|
|
|
placeholder="yourdomain.com"
|
|
|
|
|
error={error?.message}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</Section>
|
|
|
|
|
<Section>
|
|
|
|
|
<H2Title
|
|
|
|
|
title={t`Email verification`}
|
|
|
|
|
description={t`We will send your a link to verify domain ownership`}
|
|
|
|
|
/>
|
|
|
|
|
<Controller
|
|
|
|
|
name="email"
|
|
|
|
|
control={form.control}
|
|
|
|
|
render={({
|
|
|
|
|
field: { onChange, value },
|
|
|
|
|
fieldState: { error },
|
|
|
|
|
}) => (
|
2025-08-06 10:09:15 +00:00
|
|
|
<SettingsTextInput
|
2025-07-07 13:42:12 +00:00
|
|
|
instanceId="approved-access-domain-email"
|
2025-02-25 10:37:36 +00:00
|
|
|
autoComplete="off"
|
|
|
|
|
value={value.split('@')[0]}
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
fullWidth
|
|
|
|
|
error={error?.message}
|
2025-02-25 15:44:07 +00:00
|
|
|
rightAdornment={`@${domain.length !== 0 ? domain : 'your-domain.com'}`}
|
2025-02-25 10:37:36 +00:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</Section>
|
|
|
|
|
</SettingsPageContainer>
|
|
|
|
|
</SubMenuTopBarContainer>
|
|
|
|
|
</form>
|
2025-02-21 16:02:48 +00:00
|
|
|
);
|
|
|
|
|
};
|