하이어+플러스! 직원을 위해 제가 구축한 방법은 다음과 같습니다(UI - 프로필).

개요: 프로필과 관련된 모든 보기 및 기능, 호출된 모든 함수는 profileReducer에서 가져옵니다.




프로필 경로 페이지



인사이드routes > profile > profile-page.tsx호출할 함수를 지원하는 가져오기.
마운트 시 id에서 프로필을 가져옵니다. useParams에서 가져옵니다. 편집이 true로 설정된 경우 편집 프로필 구성 요소를 렌더링합니다. 그렇지 않은 경우 실제 프로필 구성 요소를 렌더링합니다.

import { useEffect } from 'react';
import { useParams } from 'react-router';
import { getProfileById } from '../../app/features/profile/profileSlice';
import BeatLoader from 'react-spinners/BeatLoader';
import { useAppSelector, useAppDispatch } from '../../app/hooks';
import Profile from '../../components/profile/profile.component';
import EditProfile from '../../components/profile/edit-profile.component';

const ProfilePage = () => {
    const { id } = useParams();
    const dispatch = useAppDispatch();
    const { isLoading, isEditting } = useAppSelector((state) => state.profile);
    useEffect(() => {
        dispatch(getProfileById(id));
    }, [id, dispatch]);
    return (
        <>
            {isLoading ? (
                <div className="text-center p-20">
                    <BeatLoader color="#ffffff" />
                </div>
            ) : (
                <>{isEditting ? <EditProfile /> : <Profile />}</>
            )}
        </>
    );
};

export default ProfilePage;




프로필 구성 요소



인사이드components > profile > profile.component.tsx호출할 함수를 지원하는 가져오기.

기능


settingEditView 토글isEditting하여 편집 프로필 구성 요소를 표시할지 여부를 결정합니다. removeItem는 (필터로) 프로젝트를 제거하고 새 결과를 project 상태로 설정합니다. UI에 정보를 표시하기 위해 currentUser를 사용합니다.

import { setEditView, setProjects } from '../../app/features/profile/profileSlice';
import { useAppDispatch, useAppSelector } from '../../app/hooks';
import Experience from '../experience/experience-component';
import Project from '../project/project-component';

const Profile = () => {
    const dispatch = useAppDispatch();
    const { profile, isEditting } = useAppSelector((state) => state.profile);
    const { currentUser } = useAppSelector((state) => state.auth);

    const settingEditView = () => {
        dispatch(setEditView(!isEditting));
    };
    const removeItem = (id: number) => {
        const newProjects = profile.projects.filter((_, i) => i !== id);
        dispatch(setProjects(newProjects));
    };

    return ( {/* removed for simplicity */} )
}


UI



프로필 정보(프로젝트, 경험, 기술, 요약, 웹 사이트 URL 등)를 렌더링합니다. 현재 사용자id와 프로필 ID가 일치하지 않으면 프로필 편집 버튼을 렌더링하지 않습니다. ProjectsExperiences는 모두 별도의 구성 요소입니다. 기본적으로 프로필을 편집할 때까지 데이터가 표시되지 않습니다.

const Profile = () => {
    {/* removed for simplicity */}
    return (
        <>
            {profile && (
                <>
                    <section style={{ backgroundColor: '#252731' }}>
                        <div className="container mx-auto py-5 px-5 md:px-0 text-right text-white flex justify-between">
                            <div className="flex justify-center text-md text-slate-200">
                                {profile.isForHire ? (
                                    <p>Actively looking for work</p>
                                ) : (
                                    <p>Not currently looking for work</p>
                                )}
                            </div>
                            {currentUser.uid !== profile.id ? null : (
                                <button
                                    onClick={settingEditView}
                                    className="underline text-md text-indigo-500"
                                >
                                    Edit Profile
                                </button>
                            )}
                        </div>
                        <div className="md:px-12 lg:px-24 max-w-7xl relative items-center w-full px-5 py-5 mx-auto">
                            <div className="mx-auto flex flex-col w-full max-w-lg mb-12 text-center">
                                <p className="mb-5 font-medium text-2xl text-white">
                                    {currentUser.displayName}
                                </p>
                                <img
                                    alt="testimonial"
                                    className="inline-block object-cover object-center w-20 h-20 mx-auto mb-8 rounded-full"
                                    src="https://picsum.photos/200"
                                />
                                <div className="flex justify-center">
                                    {profile.headline ? (
                                        <p className="text-base leading-relaxed font-color pr-2">
                                            {profile.headline}
                                        </p>
                                    ) : null}
                                    {profile.websiteURL ? (
                                        <>
                                            <a
                                                href={profile.websiteURL}
                                                className="text-base leading-relaxed text-indigo-500 border-l-2 border-gray-500 pl-2"
                                            >
                                                Live Website
                                            </a>
                                        </>
                                    ) : (
                                        <p className="font-color">No website to show</p>
                                    )}
                                </div>
                            </div>
                        </div>
                    </section>
                    <div className="divide-y divide-gray-700" key={profile.id}>
                        <section className="text-gray-600 body-font mt-2">
                            <div className="container px-10 py-20 mx-auto">
                                <div className="flex flex-col w-full mx-auto">
                                    <div className="w-full mx-auto">
                                        <h2 className="sm:text-3xl text-2xl my-5 font-bold">
                                            About Me
                                        </h2>
                                        <p className="lg:w-3/4 about-me font-color">
                                            {profile.summary ? profile.summary : 'No info to show'}
                                        </p>
                                    </div>
                                </div>
                            </div>
                        </section>
                        <section className="text-gray-600 body-font overflow-hidden">
                            <div className="container px-10 py-20 mx-auto">
                                <div className="flex flex-col w-full mx-auto">
                                    <div className="w-full mx-auto">
                                        <h2 className="sm:text-3xl text-2xl mb-5 font-bold">
                                            Skills
                                        </h2>
                                        {profile.skills.length ? (
                                            <ul className="flex flex-wrap">
                                                {profile.skills.map((skill, id) => {
                                                    return (
                                                        <li
                                                            className="mr-2 my-2 text-white px-4 py-2 rounded-3xl bg-indigo-700 cursor-pointer["
                                                            key={id}
                                                        >
                                                            {skill}
                                                        </li>
                                                    );
                                                })}
                                            </ul>
                                        ) : (
                                            <p className="font-color">No skills to show</p>
                                        )}
                                    </div>
                                </div>
                            </div>
                        </section>
                        {/* Experience starts */}
                        <section className="text-gray-600 body-font overflow-hidden">
                            <div className="container px-10 py-20 mx-auto">
                                <div className="-my-8 mx-auto">
                                    <h2 className="text-3xl my-8 mb-5 font-bold">Experience</h2>
                                    {profile.experience.length ? (
                                        <ol className="border-l-2 border-indigo-700 mt-10">
                                            {profile.experience.map((exp, index) => {
                                                return (
                                                    <Experience
                                                        experienceData={exp}
                                                        key={index}
                                                        itemIndex={index}
                                                    />
                                                );
                                            })}
                                        </ol>
                                    ) : (
                                        <p className="font-color">No experiences to show</p>
                                    )}
                                </div>
                            </div>
                        </section>
                        {/* Projects starts */}
                        <section className="text-gray-600 body-font">
                            <div className="container px-10 py-24 mx-auto">
                                <h2 className="sm:text-3xl text-2xl font-bold title-font mb-5">
                                    Projects
                                </h2>
                                {profile.projects.length ? (
                                    <div className="container py-5 mx-auto">
                                        <div className="flex flex-wrap -m-4">
                                            {profile.projects.map((proj, index) => {
                                                return (
                                                    <Project
                                                        project={proj}
                                                        key={index}
                                                        itemIndex={index}
                                                        removeItem={removeItem}
                                                    />
                                                );
                                            })}
                                        </div>
                                    </div>
                                ) : (
                                    <p className="font-color">No projects to show</p>
                                )}
                            </div>
                        </section>
                    </div>
                </>
            )}
        </>
    );
};

export default Profile;



스크린샷








프로필 구성 요소 편집



인사이드components > profile > edit-profile.component.tsx가져오기: TagsInput 태그를 추가하기 위한 반응 패키지, 기술을 추가하는 데 사용합니다. ExperiencePopupModal 프로필에 작업 경험을 추가하기 위한 팝업 모달, ProjectPopupModal 프로젝트를 추가하기 위한 팝업 모달입니다. 나머지는 적절할 때 호출하는 profileReducer의 기능입니다.

import { ChangeEvent, useState } from 'react';
import { useNavigate } from 'react-router';
import { TagsInput } from 'react-tag-input-component';
import {
    setEditView,
    setProjects,
    updateProfileById,
} from '../../app/features/profile/profileSlice';
import { useAppDispatch, useAppSelector } from '../../app/hooks';
import Experience from '../experience/experience-component';
import ExperiencePopupModal from '../modal/experience-modal.component';
import ProjectPopupModal from '../modal/project-modal.component';
import Project from '../project/project-component';


기능



토글링을 위해 업데이트해야 하는 모든 필드formFields , skillschecked를 고용 상태로 처리합니다. isOpen 열기/닫기 경험 모달용. isProjOpen 프로젝트 모달 열기/닫기용.
handleCheckedChange - isForHire 상태 전환, 결정
사용자가 일자리를 찾고 있는지 여부.
settingEditView - 편집 보기 전환
onHandleChange - formFields 상태에 대한 변경 사항 처리
onTextAreaChange - 텍스트 영역에 대한 변경 사항 처리
updateProfile - 프로필을 업데이트하고, 편집 보기를 false로 설정하고, 기본 페이지로 리디렉션합니다.
removeItem - id로 프로젝트를 제거합니다(필터 사용).
closeModal - 경험치 모달을 닫습니다.
closeProjModal - 프로젝트 모달을 닫습니다.

const EditProfile = () => {
    const { profile, isEditting } = useAppSelector((state) => state.profile);
    const { currentUser } = useAppSelector((state) => state.auth);
    const dispatch = useAppDispatch();
    const navigate = useNavigate();

    const [formFields, setFormFields] = useState({
        headline: profile.headline ? profile.headline : '',
        summary: profile.summary ? profile.summary : '',
        websiteURL: profile.websiteURL ? profile.websiteURL : '',
    });

    const [skills, setSkills] = useState<string[]>(
        profile.skills ? profile.skills : []
    );

    const [checked, setChecked] = useState<boolean>(profile.isForHire);
    const [isOpen, setIsOpen] = useState(false);
    const [isProjOpen, setIsProjOpen] = useState(false);

    const handleCheckedChange = () => {
        setChecked(!checked);
    };

    const settingEditView = () => {
        dispatch(setEditView(!isEditting));
    };

    const onHandleChange = (event: ChangeEvent<HTMLInputElement>) => {
        const { name, value } = event.target;
        setFormFields({ ...formFields, [name]: value });
    };
    const onTextAreaChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
        setFormFields({ ...formFields, summary: event.target.value });
    };

    const updateProfile = async () => {
        dispatch(
            updateProfileById({
                id: profile.id,
                headline: formFields.headline,
                summary: formFields.summary,
                isForHire: checked,
                websiteURL: formFields.websiteURL,
                skills: skills,
                experience: profile.experience,
                projects: profile.projects,
            })
        );
        dispatch(setEditView(false));
        navigate('/app');
    };
    const removeItem = (id: number) => {
        const newProjects = profile.projects.filter((_, i) => i !== id);
        dispatch(setProjects(newProjects));
    };
    const closeModal = () => {
        setIsOpen(false);
    };
    const closeProjModal = () => {
        setIsProjOpen(false);
    };

    return ( {/* removed for simplicity */} );
};

export default EditProfile;



UI



편집할 수 있는 모든 필드를 렌더링합니다. 경험 모달 또는 프로젝트 모달을 여는 버튼과 함께.

const EditProfile = () => {
    {/* removed for simplicity */}
    return (
        <>
            <section style={{ backgroundColor: '#252731' }}>
                <div className="container mx-auto py-5 text-right text-white flex justify-between">
                    <div>
                        <label
                            htmlFor="toggle-example"
                            className="flex items-center cursor-pointer relative mb-4"
                        >
                            <input
                                onChange={handleCheckedChange}
                                checked={checked}
                                type="checkbox"
                                id="toggle-example"
                                className="sr-only"
                            />
                            <div className="toggle-bg bg-gray-200 border-2 border-gray-200 h-6 w-11 rounded-full"></div>
                            <span className="ml-3 text-slate-200 text-md font-medium">
                                Are you looking for work?
                            </span>
                        </label>
                    </div>
                    <div>
                        <button
                            onClick={updateProfile}
                            className="mr-2 text-lg text-indigo-500"
                        >
                            Update
                        </button>
                        <button onClick={settingEditView}>Go Back</button>
                    </div>
                </div>
                <div className="md:px-12 lg:px-24 max-w-7xl relative items-center w-full px-5 py-5 mx-auto">
                    <div className="mx-auto flex flex-col w-full max-w-lg mb-12 text-center">
                        <p className="mb-5 font-medium text-2xl text-white">
                            {currentUser.displayName}
                        </p>
                        <img
                            alt="testimonial"
                            className="inline-block object-cover object-center w-20 h-20 mx-auto mb-8 rounded-full"
                            src="https://picsum.photos/200"
                        />
                        <div className="flex justify-center">
                            <input
                                className="font-color mr-3 primary-bg-color input-border-color block w-full px-5 py-3 mt-2 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300 apearance-none"
                                id="headline"
                                value={formFields.headline}
                                onChange={onHandleChange}
                                name="headline"
                                placeholder="e.g. Front-end Developer"
                            />
                            <input
                                className="font-color primary-bg-color input-border-color block w-full px-5 py-3 mt-2 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300 apearance-none"
                                id="websiteURL"
                                value={formFields.websiteURL}
                                onChange={onHandleChange}
                                name="websiteURL"
                                placeholder="Add website url..."
                            />
                        </div>
                    </div>
                </div>
            </section>
            <section>
                <div className="divide-y divide-gray-700">
                    <section className="text-gray-600 body-font mt-2">
                        <div className="container px-5 py-20 mx-auto">
                            <div className="flex flex-col w-full mx-auto">
                                <div className="w-full mx-auto">
                                    <h2 className="sm:text-3xl text-2xl my-5 font-bold">
                                        About Me
                                    </h2>
                                    <div>
                                        <textarea
                                            maxLength={4000}
                                            rows={5}
                                            className="font-color input-border-color secondary-bg-color block w-full px-5 py-3 mt-2 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300 apearance-none autoexpand"
                                            id="summary"
                                            name="summary"
                                            value={formFields.summary}
                                            onChange={onTextAreaChange}
                                            placeholder="Message..."
                                        ></textarea>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </section>
                    <section className="text-gray-600 body-font overflow-hidden">
                        <div className="container px-5 py-20 mx-auto">
                            <div className="flex flex-col w-full mx-auto">
                                <div className="w-full mx-auto">
                                    <h2 className="sm:text-3xl text-2xl mb-5 font-bold">
                                        Skills
                                    </h2>
                                    <TagsInput
                                        value={skills}
                                        onChange={setSkills}
                                        name="skills"
                                        placeHolder="Add skills here..."
                                    />
                                </div>
                            </div>
                        </div>
                    </section>
                    {/* Experience starts */}
                    <section className="text-gray-600 body-font overflow-hidden">
                        <div className="container px-5 py-20 mx-auto">
                            <div className="-my-8 mx-auto">
                                <h2 className="sm:text-3xl text-2xl my-5 font-bold">
                                    Experience
                                </h2>
                                <button
                                    onClick={() => setIsOpen(true)}
                                    className="block mb-5 text-white bg-indigo-700 focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-indigo-600 dark:hover:bg-indigo-700 dark:focus:ring-indigo-800"
                                    type="button"
                                    data-modal-toggle="defaultModal"
                                >
                                    Add Experience
                                </button>
                                <ExperiencePopupModal isOpen={isOpen} closeModal={closeModal} />
                                <p className="mb-5 font-color">
                                    Be sure to <b>'Update'</b> for these changes to take effect :)
                                </p>
                                {profile.experience.length ? (
                                    <ol className="border-l-2 border-indigo-700">
                                        {profile.experience.map((exp, index) => {
                                            return (
                                                <Experience
                                                    experienceData={exp}
                                                    key={index}
                                                    itemIndex={index}
                                                />
                                            );
                                        })}
                                    </ol>
                                ) : null}
                            </div>
                        </div>
                    </section>
                    {/* Projects starts */}
                    <section className="text-gray-600 body-font">
                        <div className="container px-5 py-24 mx-auto">
                            <div className="text-left mb-5">
                                <h2 className="sm:text-3xl text-2xl font-bold title-font mb-5">
                                    Projects
                                </h2>
                                <button
                                    onClick={() => setIsProjOpen(true)}
                                    className="block text-white bg-indigo-700 focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-indigo-600 dark:hover:bg-indigo-700 dark:focus:ring-indigo-800"
                                    type="button"
                                    data-modal-toggle="defaultModal"
                                >
                                    Add Project
                                </button>
                                <ProjectPopupModal
                                    isProjOpen={isProjOpen}
                                    closeProjModal={closeProjModal}
                                />
                            </div>
                            <p className="mb-5 font-color">
                                Be sure to <b>'Update'</b> for these changes to take effect :)
                            </p>
                            <div className="flex flex-wrap -m-4">
                                {profile.projects.length
                                    ? profile.projects.map((project, index) => {
                                            return (
                                                <Project
                                                    project={project}
                                                    key={index}
                                                    itemIndex={index}
                                                    removeItem={removeItem}
                                                />
                                            );
                                      })
                                    : null}
                            </div>
                        </div>
                    </section>
                </div>
            </section>
        </>
    );
};

export default EditProfile;



스크린샷



편집 상태






비편집 상태






프로젝트의 UI/프로필 부분은 여기까지입니다. 계속 지켜봐 주세요!

좋은 웹페이지 즐겨찾기