change(ui): projects revamp - project form

This commit is contained in:
Shekar Siri 2025-01-07 12:47:57 +01:00
parent c329d47c7f
commit 9374c98669
4 changed files with 174 additions and 14 deletions

View file

@ -0,0 +1,148 @@
import React, { ChangeEvent, FormEvent, useEffect } from 'react';
import styles from 'Components/Client/Sites/siteForm.module.css';
import { Icon } from 'UI';
import Project from '@/mstore/types/project';
import { projectStore, useStore } from '@/mstore';
import { Modal, Segmented, Form, Input, Button } from 'antd';
import { toast } from 'react-toastify';
import { observer } from 'mobx-react-lite';
interface Props {
project?: Project;
onClose?: (arg: any) => void;
}
function ProjectForm(props: Props) {
const { onClose } = props;
const { projectsStore } = useStore();
const project = projectsStore.instance as Project;
const loading = projectsStore.loading;
const canDelete = projectsStore.list.length > 1;
const pathname = window.location.pathname;
useEffect(() => {
if (props.project && props.project.id) {
projectsStore.initProject(props.project);
} else {
projectsStore.initProject({});
}
}, []);
const handleEdit = ({ target: { name, value } }: ChangeEvent<HTMLInputElement>) => {
projectsStore.editInstance({ [name]: value });
};
const onSubmit = (e: FormEvent) => {
e.preventDefault();
if (!projectsStore.instance) return;
if (projectsStore.instance.id && projectsStore.instance.exists()) {
projectsStore
.updateProject(projectsStore.instance.id, project)
.then((response: any) => {
if (!response || !response.errors || response.errors.size === 0) {
if (onClose) {
onClose(null);
}
if (!pathname.includes('onboarding')) {
void projectsStore.fetchList();
}
toast.success('Project updated successfully');
} else {
toast.error(response.errors[0]);
}
});
} else {
projectsStore.save(projectsStore.instance!).then((response: any) => {
if (!response || !response.errors || response.errors.size === 0) {
if (onClose) {
onClose(null);
}
// searchStore.clearSearch();
// mstore.searchStoreLive.clearSearch();
// mstore.initClient();
toast.success('Project added successfully');
} else {
toast.error(response.errors[0]);
}
});
}
};
const handleRemove = async () => {
Modal.confirm({
title: 'Project Deletion Alert',
content: 'Are you sure you want to delete this project? Deleting it will permanently remove the project, along with all associated sessions and data.',
onOk: () => {
projectsStore.removeProject(project.id!).then(() => {
if (onClose) {
onClose(null);
}
if (project.id === projectsStore.active?.id) {
projectsStore.setSiteId(projectStore.list[0].id!);
}
});
}
});
};
return (
<Form onFinish={project.validate ? onSubmit : () => null}>
<div className={styles.content}>
<Form.Item>
<label>{'Name'}</label>
<Input
placeholder="Ex. OpenReplay"
name="name"
maxLength={40}
value={project.name}
onChange={handleEdit}
className={styles.input}
/>
</Form.Item>
<Form.Item>
<label>Project Type</label>
<div>
<Segmented
options={[
{
value: 'web',
label: 'Web'
},
{
value: 'ios',
label: 'Mobile'
}
]}
value={project.platform}
onChange={(value) => {
projectsStore.editInstance({ platform: value });
}}
/>
</div>
</Form.Item>
<div className="mt-6 flex justify-between">
<Button
htmlType="submit"
type="primary"
className="float-left mr-2"
loading={loading}
disabled={!project.validate}
>
{project.exists() ? 'Update' : 'Add'}
</Button>
{project.exists() && (
<Button
variant="text"
onClick={handleRemove}
disabled={!canDelete}
>
<Icon name="trash" size="16" />
</Button>
)}
</div>
</div>
</Form>
);
}
export default observer(ProjectForm);

View file

@ -21,7 +21,12 @@ function ProjectList() {
return (
<div className="flex flex-col gap-4">
<Input.Search placeholder="Search" onSearch={onSearch} />
<Input.Search
placeholder="Search"
onSearch={onSearch}
onClear={() => setSearch('')}
allowClear
/>
<List
dataSource={list.filter((item) => item.name.toLowerCase().includes(search.toLowerCase()))}
renderItem={(item: Project) => (

View file

@ -1,8 +1,12 @@
import React from 'react';
import { Tabs } from 'antd';
import { Tabs, TabsProps } from 'antd';
import { useStore } from '@/mstore';
import { observer } from 'mobx-react-lite';
const customTabBar: TabsProps['renderTabBar'] = (props, DefaultTabBar) => (
<DefaultTabBar {...props} className="!mb-0" />
);
function ProjectTabs() {
const { projectsStore } = useStore();
const activeTab = projectsStore.config.tab;
@ -22,14 +26,14 @@ function ProjectTabs() {
return (
<Tabs
type="line"
defaultActiveKey="installation"
defaultActiveKey={tabItems[0].key}
activeKey={activeTab}
style={{ borderBottom: 'none' }}
onChange={onTabChange}
renderTabBar={customTabBar}
items={tabItems.map((tab) => ({
key: tab.key,
label: tab.label
// children: tab.content,
}))}
/>
);

View file

@ -7,8 +7,9 @@ import { useStore } from '@/mstore';
import { observer } from 'mobx-react-lite';
import { PlusIcon } from 'lucide-react';
import ProjectTabContent from 'Components/Client/Projects/ProjectTabContent';
import NewSiteForm from 'Components/Client/Sites/NewSiteForm';
import { useModal } from 'Components/ModalContext';
import ProjectForm from 'Components/Client/Projects/ProjectForm';
import Project from '@/mstore/types/project';
function Projects() {
const { projectsStore } = useStore();
@ -37,7 +38,7 @@ function Projects() {
}, [pid, tab]);
const createProject = () => {
openModal(<NewSiteForm onClose={closeModal} />, {
openModal(<ProjectForm onClose={closeModal} project={new Project()} />, {
title: 'New Project'
});
};
@ -52,23 +53,25 @@ function Projects() {
style={{ height: 'calc(100vh - 140px)' }}
extra={
<Space>
<Button type="primary" onClick={createProject} icon={<PlusIcon />}>
<Button onClick={createProject} icon={<PlusIcon />}>
Create Project
</Button>
</Space>
}
>
<Row className="items-stretch">
<Col span={6} className="border-r !p-4">
<ProjectList />
<Row className="h-full">
<Col span={6} className="border-r !p-4 flex flex-col">
<div className="flex-1 !overflow-y-auto">
<ProjectList />
</div>
</Col>
<Col span={18} className="!p-4 !overflow-hidden">
<Space className="flex justify-between">
<Col span={18} className="!p-4 flex flex-col">
<Space className="flex justify-between">
<Typography.Title level={5} className="capitalize !m-0">{project?.name}</Typography.Title>
<ProjectTabs />
</Space>
<Divider />
<div className="!overflow-y-auto">
<Divider style={{ margin: '0px' }} />
<div className="flex-1 !overflow-y-auto my-4">
{project && <ProjectTabContent />}
</div>
</Col>