Files
twenty/packages/twenty-front/src/modules/settings/accounts/hooks/useImapSmtpCaldavConnectionForm.ts
T
Félix Malfait f7cde28dd6 🔧 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 21:48:13 +02:00

183 lines
5.4 KiB
TypeScript

import { zodResolver } from '@hookform/resolvers/zod';
import { useCallback, useMemo } from 'react';
import { useForm } from 'react-hook-form';
import { useRecoilValue } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { t } from '@lingui/core/macro';
import { SettingsPath } from 'twenty-shared/types';
import {
type ConnectionParameters,
useSaveImapSmtpCaldavAccountMutation,
} from '~/generated-metadata/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { type ImapSmtpCaldavAccount } from '@/accounts/types/ImapSmtpCaldavAccount';
import { ACCOUNT_PROTOCOLS } from '@/settings/accounts/constants/AccountProtocols';
import {
connectionImapSmtpCalDav,
isProtocolConfigured,
} from '@/settings/accounts/validation-schemas/connectionImapSmtpCalDav';
import { ApolloError } from '@apollo/client';
import { isDefined } from 'twenty-shared/utils';
import {
type ConnectedImapSmtpCaldavAccount,
useConnectedImapSmtpCaldavAccount,
} from './useConnectedImapSmtpCaldavAccount';
type UseConnectionFormProps = {
isEditing?: boolean;
connectedAccountId?: string;
};
export type ConnectionFormData = {
handle: string;
} & ImapSmtpCaldavAccount;
export const useImapSmtpCaldavConnectionForm = ({
isEditing = false,
connectedAccountId,
}: UseConnectionFormProps = {}) => {
const navigate = useNavigateSettings();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const formMethods = useForm<ConnectionFormData>({
mode: 'onSubmit',
resolver: zodResolver(connectionImapSmtpCalDav),
defaultValues: {
handle: '',
IMAP: { host: '', port: 993, password: '', secure: true },
SMTP: { host: '', username: '', port: 587, password: '', secure: true },
CALDAV: {
host: '',
port: 443,
password: '',
secure: true,
username: undefined,
},
},
});
const { handleSubmit, formState, watch, reset } = formMethods;
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
const { isSubmitting } = formState;
const { connectedAccount, loading: accountLoading } =
useConnectedImapSmtpCaldavAccount(
isEditing ? connectedAccountId : undefined,
useCallback(
(account: ConnectedImapSmtpCaldavAccount | null) => {
if (isDefined(account)) {
reset({
handle: account.handle || '',
IMAP: account.connectionParameters?.IMAP || undefined,
SMTP: account.connectionParameters?.SMTP || undefined,
CALDAV: account.connectionParameters?.CALDAV || undefined,
});
}
},
[reset],
),
);
const [saveConnection, { loading: saveLoading }] =
useSaveImapSmtpCaldavAccountMutation();
const watchedValues = watch();
const getConfiguredProtocols = useCallback(
(
values: ConnectionFormData = watchedValues,
): (keyof ImapSmtpCaldavAccount)[] => {
return ACCOUNT_PROTOCOLS.filter((protocol) => {
const protocolConfig = values[protocol];
return (
protocolConfig &&
isProtocolConfigured(protocolConfig as ConnectionParameters)
);
});
},
[watchedValues],
);
const isValid = useMemo(() => {
return (
Boolean(watchedValues.handle?.trim()) &&
getConfiguredProtocols().length > 0
);
}, [getConfiguredProtocols, watchedValues.handle]);
const handleSave = useCallback(
async (formValues: ConnectionFormData): Promise<void> => {
if (!currentWorkspaceMember?.id) {
throw new Error('Workspace member ID is missing');
}
const configuredProtocols = getConfiguredProtocols(formValues);
if (configuredProtocols.length === 0) {
throw new Error('At least one protocol must be configured');
}
const connectionParameters: Partial<
Record<keyof ImapSmtpCaldavAccount, ConnectionParameters>
> = {};
configuredProtocols.forEach((protocol) => {
const protocolConfig = formValues[protocol];
if (isDefined(protocolConfig)) {
connectionParameters[protocol] = protocolConfig;
}
});
try {
await saveConnection({
variables: {
...(isEditing && connectedAccountId
? { id: connectedAccountId }
: {}),
accountOwnerId: currentWorkspaceMember.id,
handle: formValues.handle,
connectionParameters,
},
});
const successMessage = isEditing
? t`Connection successfully updated`
: t`Connection successfully created`;
enqueueSuccessSnackBar({ message: successMessage });
navigate(SettingsPath.Accounts);
} catch (error) {
enqueueErrorSnackBar({
apolloError: error instanceof ApolloError ? error : undefined,
});
}
},
[
currentWorkspaceMember?.id,
getConfiguredProtocols,
saveConnection,
isEditing,
connectedAccountId,
enqueueSuccessSnackBar,
navigate,
enqueueErrorSnackBar,
],
);
const canSave = isValid && !isSubmitting;
const loading = accountLoading || saveLoading;
return {
formMethods,
handleSave,
handleSubmit,
canSave,
isSubmitting,
loading,
connectedAccount,
};
};