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

Popis dovedností pomocí hashtagů #1083

Merged
merged 29 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c25c1ae
Add first draft of skills-to-tags conversion routine
zoul Jul 25, 2024
8a2bfb3
Add support for “tags” field in user profile
zoul Jul 25, 2024
99d4787
Add first version of hashtag select component, use in registration form
zoul Jul 26, 2024
81c9e38
Refactor types
zoul Jul 26, 2024
47554ac
Reshuffle registration form fields
zoul Jul 26, 2024
e7ea4be
Fix error in user creation endpoint
zoul Aug 15, 2024
e597cf4
Add standalone user profile field for max seniority
zoul Sep 16, 2024
a63906a
Improve copy
zoul Sep 16, 2024
d39b733
Replace old skill selection with tag select in user profile
zoul Sep 16, 2024
79f4d47
Trivial refactoring
zoul Sep 16, 2024
1eef956
Share occupation select between user profile and registration page
zoul Sep 16, 2024
2b7051e
Add first batch of new skill tags
zoul Sep 23, 2024
842ad54
Improve copy & layout consistency between registration form and profi…
zoul Sep 23, 2024
0588fd0
Fix trivial UI bugs in skill select
zoul Sep 23, 2024
663a09e
Make “background” registration field optional
zoul Sep 23, 2024
4cf672d
Display user tags from new field
zoul Sep 23, 2024
b77097f
Update user creation endpoint to use new fields
zoul Sep 23, 2024
21db28b
Improve skill select UI
zoul Sep 24, 2024
bcb7f59
Add non-immediate mode to skill select
zoul Sep 24, 2024
2c11a24
Add helper script to sort skill tags
zoul Sep 24, 2024
944d208
Update skill tags
zoul Sep 24, 2024
429398a
Try a different skill presentation option
zoul Sep 25, 2024
f3f733a
Update tag list
zoul Sep 26, 2024
8e87bdb
Limit tag list on /people to 6 lines
zoul Sep 26, 2024
8d0c10e
Fix typo
zoul Sep 26, 2024
a6ccf0f
Add temporary skill migration script
zoul Sep 26, 2024
e6cd127
Remove old tree-style skill picker
zoul Sep 26, 2024
39fb559
Improve skill layout on user profile page
zoul Sep 26, 2024
154efa1
Trivial refactoring
zoul Sep 26, 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
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
*.md
*.tf
*.json
193 changes: 61 additions & 132 deletions app/account/UserProfileTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,12 @@ import { CopyToClipboardButton } from "~/components/CopyToClipboardButton";
import { DistrictSelect } from "~/components/districts/DistrictSelect";
import { FormError } from "~/components/form/FormError";
import { usePatchedJSONResource } from "~/components/hooks/resource";
import { SkillPicker } from "~/components/SkillPicker";
import { OccupationSelect } from "~/components/profile/OccupationSelect";
import { SenioritySelect } from "~/components/profile/SenioritySelect";
import { SkillSelect } from "~/components/profile/SkillSelect";
import { type UserProfile } from "~/src/data/user-profile";
import { setFlag } from "~/src/flags";
import { absolute, Route } from "~/src/routing";
import {
decodeSkillSelection,
encodeSkillSelection,
type SkillSelection,
} from "~/src/skills/skills";
import skills from "~/src/skills/skills.json";
import { looksLikeEmailAdress } from "~/src/utils";

type SectionProps = {
Expand All @@ -42,16 +38,18 @@ export const UserProfileTab = () => {

return (
<div className="flex flex-col gap-10">
<BioSection model={model} updating={updating} onChange={setModel} />
<BasicInfoSection model={model} updating={updating} onChange={setModel} />
<PrivacySection model={model} updating={updating} onChange={setModel} />
<WorkSection model={model} updating={updating} onChange={setModel} />
<SkillSection model={model} updating={updating} onChange={setModel} />
<MapSection model={model} onChange={setModel} />
<DetailedInfoSection
model={model}
updating={updating}
onChange={setModel}
/>
</div>
);
};

const BioSection = ({ model, updating, onChange }: SectionProps) => {
const BasicInfoSection = ({ model, updating, onChange }: SectionProps) => {
return (
<section className="flex max-w-prose flex-col gap-7">
<h2 className="typo-title2">Základní informace</h2>
Expand Down Expand Up @@ -125,6 +123,57 @@ const BioSection = ({ model, updating, onChange }: SectionProps) => {
);
};

const DetailedInfoSection = ({ model, updating, onChange }: SectionProps) => (
<section className="flex max-w-prose flex-col gap-7">
<h2 className="typo-title2">Řekni nám o sobě víc</h2>

<SkillSelect
onChange={(tags) => onChange({ ...model!, tags })}
value={model?.tags ?? ""}
disabled={updating}
/>

<SenioritySelect
onChange={(maxSeniority) => onChange({ ...model!, maxSeniority })}
value={model?.maxSeniority}
disabled={updating}
/>

<OccupationSelect
onChange={(occupation) => onChange({ ...model!, occupation })}
occupation={model?.occupation}
disabled={updating}
/>

<InputWithSaveButton
onSave={(organizationName) => onChange({ ...model!, organizationName })}
id="organizationName"
label="Název organizace, kde působíš:"
saveButtonLabel="Uložit organizaci"
placeholder="název firmy, neziskové organizace, státní instituce, …"
defaultValue={model?.organizationName}
disabled={!model || updating}
/>

<InputWithSaveButton
onSave={(profileUrl) => onChange({ ...model!, profileUrl })}
id="professionalProfile"
type="url"
label="Odkaz na tvůj web nebo profesní profil:"
saveButtonLabel="Uložit odkaz"
defaultValue={model?.profileUrl}
disabled={!model || updating}
/>

<DistrictSelect
value={model?.availableInDistricts ?? ""}
onChange={(availableInDistricts) =>
onChange({ ...model!, availableInDistricts })
}
/>
</section>
);

const PrivacySection = ({ model, updating, onChange }: SectionProps) => {
const hasPublicProfile = model?.privacyFlags.includes("enablePublicProfile");

Expand Down Expand Up @@ -214,126 +263,6 @@ const PrivacySection = ({ model, updating, onChange }: SectionProps) => {
);
};

const WorkSection = ({ model, updating, onChange }: SectionProps) => {
const occupationsOptions = {
"private-sector": "Pracuji v soukromém sektoru",
"non-profit": "Pracuji v neziskové organizaci",
"state": "Pracuji ve státním sektoru",
"freelancing": "Jsem na volné noze/freelancer",
"studying": "Studuji",
"parental-leave": "Jsem na rodičovské",
"looking-for-job": "Hledám práci",
"other": "Jiné",
};

const [occupation, setOccupation] = useState("");

useEffect(() => {
setOccupation(model?.occupation ?? "");
}, [model]);

return (
<section className="flex max-w-prose flex-col gap-4">
<h2 className="typo-title2">Práce</h2>

<div className="flex flex-col gap-2">
<label htmlFor="occupation" className="block">
Čemu se aktuálně věnuješ:
</label>
<div>
{Object.entries(occupationsOptions).map(([id, label]) => (
<label key={id} className="mb-1 flex items-center">
<input
type="radio"
className="mr-3"
name="occupation"
checked={occupation == id}
disabled={updating}
onChange={() =>
onChange({
...model!,
occupation: id,
})
}
/>
<span className={occupation === id ? "font-bold" : ""}>
{label}
</span>
</label>
))}
</div>
</div>

<InputWithSaveButton
id="organizationName"
label="Název organizace, kde působíš:"
saveButtonLabel="Uložit organizaci"
placeholder="název firmy, neziskové organizace, státní instituce, …"
defaultValue={model?.organizationName}
disabled={!model || updating}
onSave={(organizationName) => onChange({ ...model!, organizationName })}
/>

<InputWithSaveButton
id="professionalProfile"
type="url"
label="Odkaz na tvůj web nebo profesní profil:"
saveButtonLabel="Uložit odkaz"
defaultValue={model?.profileUrl}
disabled={!model || updating}
onSave={(profileUrl) => onChange({ ...model!, profileUrl })}
/>
</section>
);
};


const SkillSection = ({ model, updating, onChange }: SectionProps) => {
const selection = model ? decodeSkillSelection(model.skills) : {};

// TBD: Batch updates without blocking the UI?
const onSelectionChange = (selection: SkillSelection) => {
onChange({ ...model!, skills: encodeSkillSelection(selection) });
};

return (
<section className="flex flex-col gap-4">
<h2 className="typo-title2">Co umíš?</h2>
<p className="max-w-prose">
Dej nám to vědět, ať ti můžeme různými kanály nabízet relevantnější
příležitosti.
</p>
<SkillPicker
skillMenu={skills}
selection={selection}
onChange={onSelectionChange}
disabled={updating}
/>
</section>
);
};

const MapSection = ({ model, onChange }: SectionProps) => {
return (
<section className="flex max-w-prose flex-col gap-4">
<h2 className="typo-title2">Kde býváš k zastižení?</h2>
<p>
Jsme Česko.Digital, ne Praha.Digital :) Jestli chceš, dej nám vědět, ve
kterých okresech ČR se vyskytuješ, ať můžeme lépe propojit členy a
členky Česko.Digital z různých koutů Česka.
</p>
<div>
<DistrictSelect
value={model?.availableInDistricts ?? ""}
onChange={(availableInDistricts) =>
onChange({ ...model!, availableInDistricts })
}
/>
</div>
</section>
);
};

//
// Shared Code
//
Expand Down
8 changes: 7 additions & 1 deletion app/account/me/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getUserProfileByMail,
privacyFlags,
updateUserProfile,
userSeniorities,
} from "~/src/data/user-profile";
import { normalizeEmailAddress } from "~/src/utils";

Expand All @@ -24,7 +25,8 @@ export async function POST(request: NextRequest): Promise<Response> {
const decodeRequest = record({
name: string,
email: string,
skills: string,
tags: string,
maxSeniority: optional(union(...userSeniorities)),
gdprPolicyAcceptedAt: string,
codeOfConductAcceptedAt: string,
occupation: optional(string),
Expand Down Expand Up @@ -87,6 +89,8 @@ export async function PATCH(request: NextRequest) {
contactEmail,
availableInDistricts,
bio,
tags,
maxSeniority,
occupation,
organizationName,
profileUrl,
Expand All @@ -99,6 +103,8 @@ export async function PATCH(request: NextRequest) {
contactEmail,
availableInDistricts,
bio,
tags,
maxSeniority,
occupation,
organizationName,
profileUrl,
Expand Down
Loading
Loading