Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/create project form #279

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c5d9f5c
add init forms
Primis1 Aug 27, 2024
af6c959
add: managa project settings form layout, fix: specialist detailed card
Primis1 Sep 5, 2024
5d750d2
add; layout for two forms, querries for skills and professions
Primis1 Sep 7, 2024
f0afa7f
fix: attributes query, form layout
Primis1 Sep 10, 2024
d874e1b
add: form-create-project/specialist, direction query
Primis1 Sep 14, 2024
251d1d0
add: contacts, POST query in project API
Primis1 Sep 20, 2024
4f7534e
add: not working contacts
Primis1 Sep 25, 2024
c414ae3
add: contact fields in request;
Primis1 Sep 27, 2024
3894094
fix: two forms now one
Primis1 Sep 30, 2024
1a218f3
fix: form-error, page layout
Primis1 Oct 3, 2024
c0f5ed2
add: separate rows for contacts.
Primis1 Oct 3, 2024
299e72c
Merge branch 'feature/create-project' into feature/new-create-project
Primis1 Oct 4, 2024
0069b55
fix: form, query; rm: duplicate contacts component, Attributes query
Primis1 Oct 6, 2024
fef6358
add: contacts, forms functionality; remove: attributes query;
Primis1 Oct 9, 2024
d00ec31
add: project-speciality with react hook form; refactor: component's a…
Primis1 Oct 12, 2024
56c9b85
fix: reload, component structure
Primis1 Oct 21, 2024
9322662
add: project specialists slice; rm: unused components
Primis1 Oct 25, 2024
0448f1c
fix: toggler, remove function, skill-types, reset function
Primis1 Oct 26, 2024
6baad6f
add: contacts global storage, date converter
Primis1 Oct 28, 2024
0f77440
fix merge conflict
Primis1 Oct 29, 2024
36a31be
feat(features): add create-project feature
makc-anisimov Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"sass": "^1.70.0",
"scss": "^0.2.4",
"sharp": "^0.33.2",
"uuid": "^10.0.0",
"yup": "^1.3.3",
"zod": "^3.23.8"
},
Expand All @@ -53,6 +54,7 @@
"@types/react-avatar-editor": "^13.0.3",
"@types/react-datepicker": "^6.2.0",
"@types/react-dom": "^18",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"clsx": "^2.1.0",
Expand Down
161 changes: 161 additions & 0 deletions src/entities/add-proejct-specialists/add-project-speciality.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2024-10-30_17-25-45
Поправить отступы

Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import React from 'react';
import styles from './add-specialty.module.scss';
import { LEVEL } from '@/utils/constants';
import { TProfession, TSkills } from '@/shared/types/specialty';
import IconPlus from '@/shared/assets/icons/plus-large.svg';
import { AddSpecialtyProps } from './types';
import SelectWithSearch from '@/shared/ui/select-search/select-search';
import {
getLevelName,
getSkills,
transformProfessions,
} from '@/utils/specialists-functions';
import { MainButton } from '@/shared/ui';
import { MultiSelectInput } from '@/shared/ui/multi-select-input/multi-select-input';

import {
useForm,
Controller,
} from 'react-hook-form';

export const AddProjectSpeciality: React.FC<AddSpecialtyProps> = ({
professions,
allSkills,
handleAddSpecialty,
}) => {
const { control, handleSubmit, reset, watch } = useForm({
mode: 'onChange',
});

// Watching form fields for real-time values
const selectedProfession = watch('profession');
const selectedLevel = watch('level');
const selectedSkills = watch('skills');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onSubmit: any = (data: {
profession: TProfession;
level: number;
skills: TSkills[];
}) => {
const { profession, level, skills } = data;
handleAddSpecialty({
profession,
level,
skills,
});
reset({
profession: null,
level: null,
skills: [],
});
};

const handleResetForm = () => {
reset({
profession: null,
level: null,
skills: [],
});
};

// Utilizing the watched values for field validation
const isFieldsNotFill = () => {
return (
selectedProfession === null ||
selectedLevel === null ||
selectedSkills?.length === 0
);
};

interface MultiSelectItem {
label: string;
value: number; // Adjust based on your actual value type
}

return (
<div className={styles.addSpecialty}>
<Controller
name="profession"
control={control}
rules={{ required: true }}
render={({ field }) => (
<SelectWithSearch
label="Специальность"
options={transformProfessions(professions)}
selectedValue={field.value?.specialization as unknown as string}
onValueChange={(value) =>
field.onChange(
professions.find((p) => p.specialization === value)
)
}
/>
)}
/>

<Controller
name="level"
control={control}
rules={{ required: true }}
render={({ field }) => (
<SelectWithSearch
label="Уровень квалификации"
options={LEVEL}
selectedValue={getLevelName(field.value)}
onValueChange={(value) =>
field.onChange(
LEVEL.find((lvl) => lvl.value === value)?.level as number
)
}
/>
)}
/>

<Controller
name="skills"
control={control}
rules={{ validate: (value) => value.length > 0 }}
render={({ field }) => (
<MultiSelectInput
width="100%"
name="select-skills"
label="Навыки"
description="Выберите не более 15 навыков"
maxSelections={15}
isSearchable
options={getSkills(allSkills)}
values={getSkills(field.value)}
onChange={(item: MultiSelectItem[]) =>
field.onChange(
item.map(({ label, value }) => ({
name: label,
id: value,
}))
)
}
/>
)}
/>

<div className={styles.addSpecialty__buttons}>
<MainButton
IconLeft={IconPlus}
variant="secondary"
width="regular"
onClick={handleSubmit(onSubmit)}
type="button"
disabled={isFieldsNotFill()}>
Добавить
</MainButton>

<MainButton
className={styles.addSpecialty__resetButton}
type="reset"
variant="trivial"
onClick={handleResetForm}
width="min">
Сбросить
</MainButton>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../../shared//style/variables.scss';
@import '@/shared//style/variables.scss';

.addSpecialty {
width: 100%;
Expand Down
27 changes: 27 additions & 0 deletions src/entities/add-specialists/add-specialty.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@import 'src/shared//style/variables.scss';

.addSpecialty {
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
&__buttons {
display: flex;
justify-content: space-between;
flex-direction: row;
margin-top: 16px;
}
&__resetButton {
cursor: pointer;
background: none;
padding: 0;
color: $--bg-accent-default;
font-family: $--open-sans-font;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px; /* 150% */
letter-spacing: 0.25px;
border: none;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import React, { FormEvent, useEffect, useState } from 'react';
import styles from './add-specialty.module.scss';
import SelectWithSearch from '../select-search/select-search';
import { MultiSelectInput } from '../multi-select-input/multi-select-input';
import SelectWithSearch from '@/shared/ui/select-search/select-search';
import { MultiSelectInput } from '@/shared/ui/multi-select-input/multi-select-input';
import { LEVEL } from '@/utils/constants';
import { TProfession, TSkills } from '@/shared/types/specialty';
import { AddSpecialtyProps } from './types';
import { Option } from '@/shared/types/option';
import { MainButton } from '../main-button/main-button';
import IconPlus from '@/shared/assets/icons/plus-large.svg';
import {
getLevelName,
getSkills,
transformProfessions,
} from '../../utils/specialists-functions';
import { AddSpecialtyProps } from './types';
import { MainButton } from '@/shared/ui';

export const AddSpecialty: React.FC<AddSpecialtyProps> = ({
professions,
Expand All @@ -20,13 +25,6 @@ export const AddSpecialty: React.FC<AddSpecialtyProps> = ({
const [selectedLevel, setSelectedLevel] = useState<number | null>(null);
const [skills, setSkills] = useState<TSkills[]>([]);

const transformProfessions = (profList: TProfession[]) => {
return profList?.map(({ id, specialization }) => ({
label: specialization,
value: specialization,
id,
}));
};
const handleProfessionChange = (value: string) => {
setProfession(
professions.find(
Expand All @@ -39,14 +37,6 @@ export const AddSpecialty: React.FC<AddSpecialtyProps> = ({
LEVEL.find((element) => element.value === value)?.level as number
); // обновляем состояние выбранного значения
};

const getSkills = (skills: TSkills[]) => {
return skills.map(({ id, name }) => ({
label: name,
value: id,
}));
};

const editSkills = (skills: Option[]) => {
setSkills(
skills.map(({ label, value }) => ({
Expand All @@ -55,11 +45,6 @@ export const AddSpecialty: React.FC<AddSpecialtyProps> = ({
}))
);
};
const getLevelName = (level: number) => {
if (level > 0 && level < 5) {
return LEVEL[level - 1].value;
} else return '';
};

const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
Expand All @@ -76,7 +61,7 @@ export const AddSpecialty: React.FC<AddSpecialtyProps> = ({
setSelectedLevel(null);
};
const isFieldsNotFill = () => {
return (profession === null || selectedLevel === null || skills.length === 0);
return profession === null || selectedLevel === null || skills.length === 0;
};
useEffect(() => {
if (isSuccessAddSpecialty) handleResetForm();
Expand Down Expand Up @@ -108,8 +93,8 @@ export const AddSpecialty: React.FC<AddSpecialtyProps> = ({
isSearchable
options={getSkills(allSkills)}
values={getSkills(skills)}
onChange={(item) => {
editSkills(item as Option[]);
onChange={(item: Option[]) => {
editSkills(item);
}}
/>
<div className={styles.addSpecialty__buttons}>
Expand Down
9 changes: 9 additions & 0 deletions src/entities/add-specialists/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { TProfession, TSkills, TSpeciality } from '@/shared/types/specialty';

export type AddSpecialtyProps = {
professions: TProfession[];
allSkills: TSkills[];
isLoadingAddSpecialty: boolean;
handleAddSpecialty: (data: TSpeciality) => void;
isSuccessAddSpecialty: boolean;
};
Loading