Skip to content

Commit

Permalink
tmp save
Browse files Browse the repository at this point in the history
  • Loading branch information
yaoweiprc committed Dec 17, 2024
1 parent 30931e5 commit 303b19f
Show file tree
Hide file tree
Showing 2 changed files with 330 additions and 220 deletions.
308 changes: 308 additions & 0 deletions packages/insomnia/src/ui/components/modals/project-edit-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
import React, {
type FC,
useMemo,
useState,
} from 'react';
import {
Button,
Dialog,
Heading,
Input,
Label,
Modal,
ModalOverlay,
Radio,
RadioGroup,
TextField,
} from 'react-aria-components';
import {
useNavigate,
} from 'react-router-dom';

import { getAppWebsiteBaseURL } from '../../../common/constants';
import * as models from '../../../models';
import { invariant } from '../../../utils/invariant';
import { insomniaFetch } from '../../insomniaFetch';
import { ORG_STORAGE_RULE } from '../../routes/organization';
import { Icon } from '../icon';
import { showAlert, showModal } from '.';
import { AskModal } from './ask-modal';

export enum PROJECT_EDIT_MODAL_TYPE {
NEW = 'new',
EDIT = 'edit',
};

type ProjectType = 'local' | 'remote';

const ProjectEditModal: FC<{
modalType: PROJECT_EDIT_MODAL_TYPE;
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
orgStorageRule: ORG_STORAGE_RULE;
organizationId: string;
showStorageRestrictionMessage: boolean;
}> = ({
modalType,
isOpen,
setIsOpen,
orgStorageRule,
organizationId,
showStorageRestrictionMessage,
}) => {
const [doesShowChangeStorageTypeConfirmation, setDoesShowChangeStorageTypeConfirmation] = useState(false);
const selectedStorageTypeRef
const title = useMemo(() => {
if (modalType === PROJECT_EDIT_MODAL_TYPE.NEW) {
return 'Create a New Project';
} else if (modalType === PROJECT_EDIT_MODAL_TYPE.EDIT) {
if (!doesShowChangeStorageTypeConfirmation) {
return 'Project Settings';
} else {
return 'haha';
}
} else {
throw new Error(`Invalid modal type: ${modalType}`);
}
}, [modalType, doesShowChangeStorageTypeConfirmation]);
const navigate = useNavigate();
const defaultStorageSelection = orgStorageRule === ORG_STORAGE_RULE.LOCAL_ONLY ? 'local' : 'remote';
return (<ModalOverlay
isOpen={isOpen}
onOpenChange={isOpen => setIsOpen(isOpen)}
isDismissable
className="w-full h-[--visual-viewport-height] fixed z-10 top-0 left-0 flex items-center justify-center bg-black/30"
>
<Modal className="max-w-2xl w-full rounded-md border border-solid border-[--hl-sm] p-[--padding-lg] max-h-full bg-[--color-bg] text-[--color-font]">
<Dialog className="outline-none" aria-label='Create or update dialog'>
{({ close }) => (
<div className='flex flex-col gap-4'>
<div className='flex gap-2 items-center justify-between'>
<Heading slot="title" className='text-2xl'>{title}</Heading>
<Button
className="flex flex-shrink-0 items-center justify-center aspect-square h-6 aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] focus:ring-inset ring-1 ring-transparent focus:ring-[--hl-md] transition-all text-sm"
onPress={close}
>
<Icon icon="x" />
</Button>
</div>
<form
className='flex flex-col gap-4'
onSubmit={e => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const type = formData.get('type');

if (!type) {
showAlert({
title: 'Project type not selected',
message: 'Please select a project type before continuing',
});
return;
}

const name = formData.get('name');

createNewProject({
organizationId,
name: (typeof name === 'string') ? name : 'My project',
projectType: type as ProjectType,
}).then(
newProjectId => {
navigate(`/organization/${organizationId}/project/${newProjectId}`);
},
err => {
const errMsg = err.message;
if (errMsg === 'NEEDS_TO_UPGRADE') {
showModal(AskModal, {
title: 'Upgrade your plan',
message: 'You are currently on the Free plan where you can invite as many collaborators as you want as long as you don\'t have more than one project. Since you have more than one project, you need to upgrade to "Individual" or above to continue.',
yesText: 'Upgrade',
noText: 'Cancel',
onDone: async (isYes: boolean) => {
if (isYes) {
window.main.openInBrowser(`${getAppWebsiteBaseURL()}/app/subscription/update?plan=individual`);
}
},
});
} else if (errMsg === 'FORBIDDEN') {
showAlert({
title: 'Could not create project.',
message: 'You do not have permission to create a project in this organization.',
});
} else {
showAlert({
title: 'Could not create project.',
message: errMsg,
});
}
},
);

close();
}}
>
<TextField
autoFocus
name="name"
defaultValue="My project"
className="group relative flex-1 flex flex-col gap-2"
>
<Label className='text-sm text-[--hl]'>
Project name
</Label>
<Input
placeholder="My project"
className="py-1 placeholder:italic w-full pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors"
/>
</TextField>
<RadioGroup name="type" defaultValue={defaultStorageSelection} className="flex flex-col gap-2">
<Label className="text-sm text-[--hl]">
Project type
</Label>
<div className="flex gap-2">
<Radio
isDisabled={orgStorageRule === ORG_STORAGE_RULE.LOCAL_ONLY}
value="remote"
className="flex-1 data-[selected]:border-[--color-surprise] data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] data-[disabled]:opacity-25 hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<div className='flex items-center gap-2'>
<Icon icon="globe" />
<Heading className="text-lg font-bold">Cloud Sync</Heading>
</div>
<p className='pt-2'>
Encrypted and synced securely to the cloud, ideal for out of the box collaboration.
</p>
</Radio>
<Radio
isDisabled={orgStorageRule === ORG_STORAGE_RULE.CLOUD_ONLY}
value="local"
className="flex-1 data-[selected]:border-[--color-surprise] data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] data-[disabled]:opacity-25 hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<div className="flex items-center gap-2">
<Icon icon="laptop" />
<Heading className="text-lg font-bold">Local Vault</Heading>
</div>
<p className="pt-2">
Stored locally only with no cloud. Ideal when collaboration is not needed.
</p>
</Radio>
<Radio
isDisabled={orgStorageRule === ORG_STORAGE_RULE.CLOUD_ONLY}
value="local"
className="flex-1 data-[selected]:border-[--color-surprise] data-[selected]:ring-2 data-[selected]:ring-[--color-surprise] data-[disabled]:opacity-25 hover:bg-[--hl-xs] focus:bg-[--hl-sm] border border-solid border-[--hl-md] rounded p-4 focus:outline-none transition-colors"
>
<div className="flex items-center gap-2">
<Icon icon="laptop" />
<Heading className="text-lg font-bold">Local Vault</Heading>
</div>
<p className="pt-2">
Stored locally only with no cloud. Ideal when collaboration is not needed.
</p>
</Radio>
</div>
</RadioGroup>
<div className="flex justify-between gap-2 items-center">
<div className="flex items-center gap-2 text-sm">
<Icon icon="info-circle" />
<span>
{showStorageRestrictionMessage && `The organization owner mandates that projects must be created and stored ${orgStorageRule.split('_').join(' ')}.`} You can optionally enable Git Sync
</span>
</div>
<div className='flex items-center gap-2'>
<Button
onPress={close}
className="hover:no-underline hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font] transition-colors rounded-sm"
>
Cancel
</Button>
<Button
type="submit"
className="hover:no-underline bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
>
Create
</Button>
</div>
</div>
</form>
</div>
)}
</Dialog>
</Modal>
</ModalOverlay>);
};

ProjectEditModal.displayName = 'NewProjectModal';

async function createNewProject({
organizationId,
name,
projectType,
}: {
organizationId: string;
name: string;
projectType: ProjectType;
}) {
invariant(organizationId, 'Organization ID is required');
invariant(typeof name === 'string', 'Name is required');
invariant(projectType === 'local' || projectType === 'remote', 'Project type is required');

const user = await models.userSession.getOrCreate();
const sessionId = user.id;
invariant(sessionId, 'User must be logged in to create a project');

if (projectType === 'local') {
const project = await models.project.create({
name,
parentId: organizationId,
});
return project._id;
}

try {
const newCloudProject = await insomniaFetch<{
id: string;
name: string;
} | {
error: string;
message?: string;
}>({
path: `/v1/organizations/${organizationId}/team-projects`,
method: 'POST',
data: {
name,
},
sessionId,
});

if (!newCloudProject || 'error' in newCloudProject) {
let error = 'An unexpected error occurred while creating the project. Please try again.';
if (newCloudProject.error === 'FORBIDDEN') {
error = newCloudProject.error;
}

if (newCloudProject.error === 'NEEDS_TO_UPGRADE') {
error = 'Upgrade your account in order to create new Cloud Projects.';
}

if (newCloudProject.error === 'PROJECT_STORAGE_RESTRICTION') {
error = newCloudProject.message ?? 'The owner of the organization allows only Local Vault project creation.';
}

throw new Error(error);
}

const project = await models.project.create({
_id: newCloudProject.id,
name: newCloudProject.name,
remoteId: newCloudProject.id,
parentId: organizationId,
});

return project._id;
} catch (err) {
throw new Error(err instanceof Error ? err.message : `An unexpected error occurred while creating the project. Please try again. ${err}`);
}
}

export default ProjectEditModal;
Loading

0 comments on commit 303b19f

Please sign in to comment.