diff --git a/src/app/bounties/Filter.js b/src/app/bounties/Filter.js index bfa3907..f326202 100644 --- a/src/app/bounties/Filter.js +++ b/src/app/bounties/Filter.js @@ -20,32 +20,32 @@ import clsx from 'clsx'; import { motion } from 'framer-motion'; import Image from 'next/image'; import { useRouter, useSearchParams, usePathname } from 'next/navigation'; -import { useMemo, useState } from 'react'; +import { useState } from 'react'; import { ChevronUpIcon } from '@/components/icon/solid'; -import { ReactSelect } from '@/components/Select/ReactSelect'; import { createQueryString } from '@/utils'; import { bountyFilterList } from '#/lib/bountyFilterList'; -import { useAllSkills } from '#/state/application/hooks'; +import SelectSkills from '#/shared/components/SelectSkills'; + export function SkillsFilter() { const searchParams = useSearchParams(); const { replace } = useRouter(); const pathname = usePathname(); - const skillOpts = useAllSkills(); const [open, setOpen] = useState(true); - const defaultValue = useMemo(() => { - const current = searchParams.get('skills')?.split(','); - return current?.length > 0 ? current.map(ci => skillOpts?.filter(f => f.value === Number(ci))).flat(1) : null; - }, [skillOpts, searchParams]); + const selectedSkills = + searchParams + .get('skills') + ?.split(',') + .map(id => Number(id)) || []; const handleSearch = term => { const params = new URLSearchParams(searchParams); params.set('page', '1'); - if (term) { + if (term && term.length > 0) { params.set('skills', term); } else { params.delete('skills'); @@ -82,18 +82,7 @@ export function SkillsFilter() { closed: { opacity: 0, height: '0' }, }} > - { - const _skills = e.map(i => i.value); - handleSearch(_skills); - }} - placeholder={'Select skills'} - className="no-bg hauto" - /> +
diff --git a/src/app/creator/build/[type]/[id]/page.js b/src/app/creator/build/[type]/[id]/page.js index 572e4b1..f794131 100644 --- a/src/app/creator/build/[type]/[id]/page.js +++ b/src/app/creator/build/[type]/[id]/page.js @@ -29,7 +29,9 @@ import { BASE_INPUT_STYLE } from '@/constants/config'; import { get } from '@/utils/request'; import { publishBounty, editBounty } from '#/services/creator'; -import { useAllSkills, useConfig, useMediaUrl } from '#/state/application/hooks'; +import SelectSkills from '#/shared/components/SelectSkills'; +import { useConfig, useMediaUrl } from '#/state/application/hooks'; + const options = [ { @@ -61,7 +63,6 @@ export default function Page({params: { id }}) { const router = useRouter(); const config = useConfig(); const mediaUrl = useMediaUrl(); - const allSkills = useAllSkills(); const [ecosystem, setEcosystem] = useState(''); const [skills, setSkills] = useState([]); @@ -228,19 +229,7 @@ export default function Page({params: { id }}) {
Required Skills Select up to 3 items
- allSkills?.find(f => f.value === i))} - isMulti - name="skills" - options={allSkills} - className="no-bg" - onChange={e => { - const _skills = e.map(i => i.value); - setSkills(_skills); - }} - /> - - {/* */} +
diff --git a/src/app/profile/MySkill.js b/src/app/profile/MySkill.js index b2ab8e6..ffc1d84 100644 --- a/src/app/profile/MySkill.js +++ b/src/app/profile/MySkill.js @@ -22,7 +22,6 @@ import { Button } from '@/components/Button'; import { UploadIcon } from '@/components/Icons'; import Loader from '@/components/Loader'; import { Select } from '@/components/Select'; -import { ReactSelect } from '@/components/Select/ReactSelect'; // import { Transition } from '@/components/control'; // import { CheckIcon, XMarkIcon } from '@/components/icon/solid' import { BASE_INPUT_STYLE } from '@/constants/config'; @@ -31,20 +30,11 @@ import { classNames } from '@/utils'; import { ProfileTitle, ProfileLabel } from '#/domain/profile/widgets/blocks'; import { EXPERIENCE_OPTIONS } from '#/lib/user'; import { upload } from '#/services/common'; -import { useAllSkills, useConfig } from '#/state/application/hooks'; +import SelectSkills from '#/shared/components/SelectSkills'; +import { useConfig } from '#/state/application/hooks'; -export function MySkill({ forms, set, formsError }) { - const allSkills = useAllSkills(); - // const [openSkills, setOpenSkills] = useState(false) - // const [skillsInputValue, setSkillsInputValue] = useState('') - // // const [delFlag, setDelFlag] = useState(0) - // const [searchSkillOpts, setSearchSkillOpts] = useState< - // { - // key: number - // name: string - // }[] - // >([]) +export function MySkill({ forms, set, formsError }) { const [uploading, setUploading] = useState(false); const [uploadFileSizeError, setUploadFileSizeError] = useState(false); const uploadRef = useRef(null); @@ -59,20 +49,6 @@ export function MySkill({ forms, set, formsError }) { })); }, [allOpts]); - // const skillOpts = useMemo(() => { - // return allOpts?.skills?.map(i => ({ - // key: i.id, - // name: i.name, - // })) - // }, [allOpts]) - - // useEffect(() => { - // if (skillOpts) { - // const _search = skillOpts.filter(f => f.name.toLocaleLowerCase().includes(skillsInputValue.toLocaleLowerCase())) - // setSearchSkillOpts(_search) - // } - // }, [skillsInputValue, skillOpts]) - const handleFileChange = event => { const files = event.target.files; if (files && files[0]) { @@ -121,23 +97,7 @@ export function MySkill({ forms, set, formsError }) { Skills * - allSkills?.find(f => f.value === i))} - isMulti - name="skills" - options={allSkills} - className="no-bg" - styles={{ - control: () => ({ - height: 'auto !important', - minHeight: '40px', - }), - }} - onChange={e => { - const _skills = e.map(i => i.value); - set('skills', _skills); - }} - /> + set('skills',_skills)} /> {/*
state.shilling.requiredSkills); return (
Required Skills:
- { - const _skills = e.map(i => i.value); - dispatch(setRequiredSkills(_skills)); - }} - className="react-select-noborder !h-12 border-0 " - limit={3} - /> + dispatch(setRequiredSkills(_skills))} className="react-select-noborder !h-12 border-0 "/>
Experience:
diff --git a/src/app/shilling/[id]/Author.js b/src/app/shilling/[id]/Author.js index 3cf00c7..49b04cf 100644 --- a/src/app/shilling/[id]/Author.js +++ b/src/app/shilling/[id]/Author.js @@ -26,18 +26,18 @@ import Avatar from '@/components/Avatar'; import { Button } from '@/components/Button'; import { RepositioningIcon, TriangleIcon, InformationIcon } from '@/components/Icons'; import { Modal } from '@/components/Modal'; -import { ReactSelect } from '@/components/Select/ReactSelect'; import { baseInputStyles } from '#/domain/profile/widgets/blocks'; import { countries } from '#/lib/countries'; import { applyGetContact } from '#/services/shilling'; import { useDetailsPermission } from '#/services/shilling/hooks'; -import { useAllSkills } from '#/state/application/hooks'; +import SelectSkills from '#/shared/components/SelectSkills'; import { ContactModal } from '../ContactModal'; import { HireConfirmModal } from '../HireConfirmModal'; import { HireOnChainModal } from '../HireOnChainModal'; + export function Author({ data }) { const { status } = useSession(); const router = useRouter(); @@ -52,7 +52,6 @@ export function Author({ data }) { const [applyLoading, setApplyLoading] = useState(false); const [openToken, setOpenToken] = useState(false); const [token, setToken] = useState(''); - const skills = useAllSkills(); const [selectSkills, setSelectSkills] = useState(); const getContact = token => { setOpenToken(false); @@ -139,15 +138,7 @@ export function Author({ data }) {

Choose the Skill you interested

- { - const _skills = e.map(i => i.value); - setSelectSkills(_skills); - }} - className="react-select-container !min-h-12 border-0" - /> +

The developer need you provide your introduction & demand

diff --git a/src/app/shilling/[id]/page.js b/src/app/shilling/[id]/page.js index 7d502ac..ff696be 100644 --- a/src/app/shilling/[id]/page.js +++ b/src/app/shilling/[id]/page.js @@ -15,23 +15,21 @@ */ 'use client'; -import clsx from 'clsx'; import { useEffect, useState } from 'react'; import { OViewer } from '@/components/MarkDown'; import { Skeleton } from '@/components/Skeleton/details'; import { fromNow } from '@/utils/date'; -import SkillInsight from '#/domain/skill/widgets/skill-insight'; +import SkillOverviewView from '#/domain/skill/views/skill-overview'; +import SkillLabel from '#/domain/skill/widgets/skill-label'; import { ownedNFTs } from '#/services/common'; import { useDetails } from '#/services/shilling/hooks'; -import { useAllSkills } from '#/state/application/hooks'; import { Author } from './Author'; import { Header } from './Header'; export default function Page({ params }) { - const skills = useAllSkills(); const { data, loading } = useDetails(params.id); const [nfts, setNfts] = useState([]); @@ -45,8 +43,6 @@ export default function Page({ params }) { } }, [data]); - console.log(nfts); - return loading ? ( ) : ( @@ -85,89 +81,10 @@ export default function Page({ params }) { {data?.rec && }
{/* */} -
- {data?.skill_datas.map(i => ( - - {skills?.find(f => f.value === Number(i.skill))?.label} - - ))} -
+
-
-
Skills
-
- {data?.skill_datas.map(i => ( -
-
-
-
{skills?.find(f => f.value === i.skill)?.label}
-
- {i.level && ( - - {i.level} - - )} - - Usage time {i.time}Y -
-
- - - - -
-
-

Estimated cost

-

- - ${Number(i.cost_min).toFixed(2)}-${Number(i.cost_max).toFixed(2)} - {' '} - / Hourly -

-
-
- ))} -
-
+
- {data?.aspecta_show && ( - - )} {data?.onchain_show && nfts.length > 0 && (
diff --git a/src/domain/skill/repository.js b/src/domain/skill/repository.js new file mode 100644 index 0000000..0775f6a --- /dev/null +++ b/src/domain/skill/repository.js @@ -0,0 +1,25 @@ +/** + * Copyright 2024 OpenBuild + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import httpClient from '@/utils/http'; + +async function getUserSkills({ userId }) { + return await httpClient.get(`hub/general/skills/${userId}`); +} + +export { + getUserSkills, +}; diff --git a/src/domain/skill/views/skill-overview/SkillOverview.js b/src/domain/skill/views/skill-overview/SkillOverview.js index 7dae0cd..73e89e0 100644 --- a/src/domain/skill/views/skill-overview/SkillOverview.js +++ b/src/domain/skill/views/skill-overview/SkillOverview.js @@ -15,31 +15,38 @@ */ import clsx from 'clsx'; -import useSWR from 'swr'; +import { useState } from 'react'; import { NoData } from '@/components/NoData'; -import { fetcher } from '@/utils/request'; +import useMounted from '#/shared/hooks/useMounted'; import { useAllSkills } from '#/state/application/hooks'; +import { getUserSkills } from '../../repository'; import SkillInsight from '../../widgets/skill-insight'; import SkillCircle from './SkillCircle'; function SkillOverviewView({ userId }) { const skills = useAllSkills(); - const { data } = useSWR(userId ? `ts/v1/hub/general/skills/${userId}` : null, fetcher); - const userSkills = data?.skill_datas || []; + const [data, setData] = useState([]); + + useMounted(() => { + getUserSkills({userId}) + .then(res => { + setData(res?.data); + }); + }); return (
- {userSkills.length > 0 ? ( + {data.skill_datas && data.skill_datas.length > 0 ? ( <>

Skills

- {userSkills.map(i => ( + {data.skill_datas.map(i => (
diff --git a/src/domain/skill/widgets/skill-label/SkillLabel.js b/src/domain/skill/widgets/skill-label/SkillLabel.js new file mode 100644 index 0000000..94a36ad --- /dev/null +++ b/src/domain/skill/widgets/skill-label/SkillLabel.js @@ -0,0 +1,47 @@ +/** + * Copyright 2024 OpenBuild + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useState } from 'react'; + +import useMounted from '#/shared/hooks/useMounted'; +import { useAllSkills } from '#/state/application/hooks'; + +import { getUserSkills } from '../../repository'; + +function SkillLabel({ userId }) { + const allSkills = useAllSkills(); + const [skills, setSkills] = useState([]); + + useMounted(() => { + getUserSkills({ userId }).then(res => { + setSkills(res?.data?.skill_datas || []); + }); + }); + return ( +
+ {skills.map(i => ( + + {allSkills?.find(f => f.value === Number(i.skill))?.label} + + ))} +
+ ); +} + +export default SkillLabel; diff --git a/src/domain/skill/widgets/skill-label/index.js b/src/domain/skill/widgets/skill-label/index.js new file mode 100644 index 0000000..e7c647d --- /dev/null +++ b/src/domain/skill/widgets/skill-label/index.js @@ -0,0 +1,17 @@ +/** + * Copyright 2024 OpenBuild + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './SkillLabel'; diff --git a/src/shared/components/SelectSkills/index.js b/src/shared/components/SelectSkills/index.js index c46604d..44e6237 100644 --- a/src/shared/components/SelectSkills/index.js +++ b/src/shared/components/SelectSkills/index.js @@ -15,112 +15,32 @@ */ 'use client'; +import { ReactSelect } from '@/components/Select/ReactSelect'; -import { useMemo, useState, Fragment, useEffect } from 'react'; +import { useAllSkills } from '#/state/application/hooks'; -import { classNames, arrRemove } from '@/utils'; +export default function SelectSkills({ selectedSkills, onChange, limit = Infinity, className = 'no-bg hauto' }) { + const allSkills = useAllSkills(); -import { baseInputStyles } from '#/domain/profile/widgets/blocks'; -import { useConfig } from '#/state/application/hooks'; + const selectedOptions = selectedSkills?.map(id => + allSkills?.find(skill => skill.value === id) + ) || []; -import { Transition } from '../control/headlessui'; -import { CheckIcon, XMarkIcon } from '../icon/solid'; + const handleChange = selectedOptions => { + const limitedOptions = selectedOptions.slice(0, limit); + const selectedIds = limitedOptions.map(option => option.value); + onChange(selectedIds); + }; -export default function SelectSkills({ skills, setSkills }) { - const config = useConfig(); - - const allOpts = config?.find(f => f.config_id === 3)?.config_value; - - const skillOpts = useMemo(() => { - return allOpts?.skills?.map(i => ({ - key: i.id, - name: i.name, - })); - }, [allOpts]); - - const [openSkills, setOpenSkills] = useState(false); - const [skillsInputValue, setSkillsInputValue] = useState(''); - // const [delFlag, setDelFlag] = useState(0) - const [searchSkillOpts, setSearchSkillOpts] = useState([]); - - useEffect(() => { - if (skillOpts) { - const _search = skillOpts.filter(f => f.name.toLocaleLowerCase().includes(skillsInputValue.toLocaleLowerCase())); - setSearchSkillOpts(_search); - } - }, [skillsInputValue, skillOpts]); return ( -
-
- {skills?.map((i, k) => { - const finded = skillOpts?.find(f => f.key === i); - return ( - finded && ( -
- {finded?.name} - { - const _sk = [...skills]; - setSkills(arrRemove(_sk, i)); - }} - className="ml-3 h-4 w-4 cursor-pointer text-gray-50" - /> -
- ) - ); - })} - setOpenSkills(false)} - onFocus={() => setOpenSkills(true)} - value={skillsInputValue} - onChange={e => setSkillsInputValue(e.target.value)} - className={classNames(baseInputStyles, 'w-auto border-0')} - /> -
- -
- {searchSkillOpts && - searchSkillOpts.map((o, oIdx) => ( -
{ - const _skills = skills || []; - _skills.push(o.key); - const _setSkills = Array.from(new Set(_skills)); - setSkills(_setSkills); - setSkillsInputValue(''); - }} - > -
- {o.name} - {skills?.filter(f => f === o.key).length > 0 ? ( - - - ) : null} -
-
- ))} - {searchSkillOpts.length === 0 &&
No items
} -
-
-
+ ); }