From c25c1ae237c4b971852db0014357c580c500c335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Znamen=C3=A1=C4=8Dek?= Date: Thu, 25 Jul 2024 09:20:22 +0200 Subject: [PATCH 01/29] Add first draft of skills-to-tags conversion routine --- src/skills/skills.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/skills/skills.ts b/src/skills/skills.ts index 543743dc9..cef386a09 100644 --- a/src/skills/skills.ts +++ b/src/skills/skills.ts @@ -227,7 +227,11 @@ Vývoj / WordPress -> #wordpress Projektové řízení / Business model a development -> #projektové-řízení Projektové řízení / Finance -> #finance Projektové řízení / Fundraising -> #fundraising +<<<<<<< HEAD Projektové řízení / Product Owner -> #product-owner +======= +Projektové řízení / Product Owner -> #ProductOwner +>>>>>>> ec1e578b (Add first draft of skills-to-tags conversion routine) Projektové řízení / Scrum Master -> #scrum Personalistika / Community management -> #komunity Personalistika / Recruitment -> #hr From 8a2bfb3580f83c593b0975cb29c2b89c5c86d1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Znamen=C3=A1=C4=8Dek?= Date: Thu, 25 Jul 2024 11:03:38 +0200 Subject: [PATCH 02/29] =?UTF-8?q?Add=20support=20for=20=E2=80=9Ctags?= =?UTF-8?q?=E2=80=9D=20field=20in=20user=20profile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/user-profile.test.ts | 3 +++ src/data/user-profile.ts | 5 +++++ src/skills/skills.ts | 4 ---- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/data/user-profile.test.ts b/src/data/user-profile.test.ts index f567aec48..b3f69e779 100644 --- a/src/data/user-profile.test.ts +++ b/src/data/user-profile.test.ts @@ -24,6 +24,7 @@ test("Decode user with no skills", () => { email: "john@smith.name", contactEmail: undefined, skills: "", + tags: "", occupation: undefined, organizationName: undefined, profileUrl: undefined, @@ -68,6 +69,7 @@ test("Decode Slack Users relation", () => { email: "john@smith.name", contactEmail: "john2@smith.name", skills: "Dev / Go", + tags: "", occupation: undefined, organizationName: undefined, profileUrl: undefined, @@ -109,6 +111,7 @@ test("Decode Slack Users relation", () => { email: "john@smith.name", contactEmail: undefined, skills: "Dev / Go", + tags: "", occupation: undefined, organizationName: undefined, profileUrl: undefined, diff --git a/src/data/user-profile.ts b/src/data/user-profile.ts index 127eaaace..16d8e300b 100644 --- a/src/data/user-profile.ts +++ b/src/data/user-profile.ts @@ -48,6 +48,7 @@ export interface Schema extends FieldSet { name: string; email: string; competencies: string; + tags: string; occupation: string; organizationName: string; profileUrl: string; @@ -89,6 +90,7 @@ export const decodeUserProfile = record({ contactEmail: relationToZeroOrOne, // TBD: Once the skill migration is over, rename skills: field("competencies", withDefault(string, "")), + tags: withDefault(string, ""), occupation: optional(string), organizationName: optional(string), // If profile URL is malformed, parse as `undefined` instead of throwing @@ -125,6 +127,7 @@ export function encodeUserProfile( email: profile.email, contactEmail: profile.contactEmail, competencies: profile.skills, + tags: profile.tags, occupation: profile.occupation, organizationName: profile.organizationName, profileUrl: profile.profileUrl, @@ -198,6 +201,7 @@ export async function updateUserProfile( UserProfile, | "name" | "skills" + | "tags" | "slackUserRelationId" | "state" | "createdAt" @@ -227,6 +231,7 @@ export async function createUserProfile( | "name" | "email" | "skills" + | "tags" | "occupation" | "organizationName" | "profileUrl" diff --git a/src/skills/skills.ts b/src/skills/skills.ts index cef386a09..543743dc9 100644 --- a/src/skills/skills.ts +++ b/src/skills/skills.ts @@ -227,11 +227,7 @@ Vývoj / WordPress -> #wordpress Projektové řízení / Business model a development -> #projektové-řízení Projektové řízení / Finance -> #finance Projektové řízení / Fundraising -> #fundraising -<<<<<<< HEAD Projektové řízení / Product Owner -> #product-owner -======= -Projektové řízení / Product Owner -> #ProductOwner ->>>>>>> ec1e578b (Add first draft of skills-to-tags conversion routine) Projektové řízení / Scrum Master -> #scrum Personalistika / Community management -> #komunity Personalistika / Recruitment -> #hr From 99d47873174ef226f401f59c81dd91432c97cbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Znamen=C3=A1=C4=8Dek?= Date: Fri, 26 Jul 2024 11:24:22 +0200 Subject: [PATCH 03/29] Add first version of hashtag select component, use in registration form --- app/join/SignUpForm.tsx | 28 ++++--------- app/join/form-state.test.ts | 4 +- app/join/form-state.ts | 13 +++--- components/HashtagSelect.tsx | 80 ++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 components/HashtagSelect.tsx diff --git a/app/join/SignUpForm.tsx b/app/join/SignUpForm.tsx index dd3b6272c..bf846a7bc 100644 --- a/app/join/SignUpForm.tsx +++ b/app/join/SignUpForm.tsx @@ -8,10 +8,8 @@ import { boolean, record } from "typescript-json-decoder"; import { DistrictSelect } from "~/components/districts/DistrictSelect"; import { FormError } from "~/components/form/FormError"; -import { SkillPicker } from "~/components/SkillPicker"; +import { HashtagSelect } from "~/components/HashtagSelect"; import { trackCustomEvent } from "~/src/plausible/events"; -import { encodeSkillSelection, type SkillMenu } from "~/src/skills/skills"; -import skillMenu from "~/src/skills/skills.json"; import { ContentType, looksLikeEmailAdress } from "~/src/utils"; import ArrowIllustration from "./arrows.svg"; @@ -69,7 +67,7 @@ export const SignUpForm = ({ defaultEmail }: Props) => {
- + @@ -259,21 +257,12 @@ const OccupationSelect: FormSection = ({ state, onChange }) => { // Skills section // -type SkillSectionProps = FormSectionProps & { - skillMenu: SkillMenu; -}; - -const SkillSection: React.FC = ({ - state, - skillMenu, - onChange, -}) => { +const SkillSection: React.FC = ({ state, onChange }) => { return (

Dovednosti, které můžeš komunitě nabídnout -

Díky co nejpřesnějšímu vyplnění tvého zaměření a úrovně zkušeností tě @@ -281,11 +270,9 @@ const SkillSection: React.FC = ({ typu aktivity nebo projektu. Vyplň vše, co tě zajímá, včetně oblastí, ve kterých se chceš rozvíjet.

- onChange({ ...state, skills })} + onChange({ ...state, tags })} />
@@ -573,11 +560,10 @@ const TextArea: React.FC = ({ // async function createUserProfile(data: RegistrationData): Promise { - const payload = { ...data, skills: encodeSkillSelection(data.skills) }; try { const response = await fetch("/account/me", { method: "POST", - body: JSON.stringify(payload, null, 2), + body: JSON.stringify(data, null, 2), headers: { "Content-Type": ContentType.json }, }); return response.ok; diff --git a/app/join/form-state.test.ts b/app/join/form-state.test.ts index 2ac3ca623..4bd0393fb 100644 --- a/app/join/form-state.test.ts +++ b/app/join/form-state.test.ts @@ -11,7 +11,7 @@ test("Validate correctly filled form", () => { organizationName: "", profileUrl: "", occupation: "whatever", - skills: { Marketing: { Copywriting: null } }, + tags: "#fake", availableInDistricts: "Praha, Brno", bio: "Ahoj, já jsem Aloisie a chci se zapojit do vašeho projektu.", privacyConsent: true, @@ -27,7 +27,7 @@ test("Validate correctly filled form", () => { validatedData: { name: "Aloisie Citronová", email: "aloisie@cesko.digital", - skills: { Marketing: { Copywriting: null } }, + tags: "#fake", occupation: "whatever", availableInDistricts: "Praha, Brno", bio: "Ahoj, já jsem Aloisie a chci se zapojit do vašeho projektu.", diff --git a/app/join/form-state.ts b/app/join/form-state.ts index c70134fdd..5e62b2618 100644 --- a/app/join/form-state.ts +++ b/app/join/form-state.ts @@ -1,5 +1,4 @@ import { type PrivacyFlags } from "~/src/data/user-profile"; -import { type SkillSelection } from "~/src/skills/skills"; import { looksLikeEmailAdress } from "~/src/utils"; export type SubmissionState = @@ -11,7 +10,7 @@ export type SubmissionState = export type RegistrationData = { name: string; email: string; - skills: SkillSelection; + tags: string; occupation: string; organizationName?: string; privacyFlags: PrivacyFlags; @@ -29,7 +28,7 @@ export type FormState = { occupation: string; organizationName: string; profileUrl: string; - skills: SkillSelection; + tags: string; privacyConsent: boolean; availableInDistricts: string; bio: string; @@ -46,7 +45,7 @@ export const emptyFormState: FormState = { organizationName: "", occupation: "", profileUrl: "", - skills: {}, + tags: "", availableInDistricts: "", bio: "", privacyConsent: false, @@ -67,7 +66,7 @@ export function validateForm( const { name, email, - skills, + tags, privacyConsent, gdprConsent, cocConsent, @@ -80,8 +79,6 @@ export function validateForm( return error("Je třeba vyplnit email."); } else if (!looksLikeEmailAdress(email)) { return error("V e-mailové adrese je nějaká chyba."); - } else if (Object.entries(skills).length === 0) { - return error("Je třeba vyplnit aspoň jednu dovednost."); } else if (!privacyConsent) { return error("Je třeba odsouhlasit podmínky zpracování osobních údajů."); } else if (!gdprConsent) { @@ -111,7 +108,7 @@ export function validateForm( validatedData: { name, email, - skills, + tags, organizationName, availableInDistricts, bio, diff --git a/components/HashtagSelect.tsx b/components/HashtagSelect.tsx new file mode 100644 index 000000000..297ab9f1a --- /dev/null +++ b/components/HashtagSelect.tsx @@ -0,0 +1,80 @@ +import Select from "react-select"; + +type Option = { + label: string; + value: string; +}; + +export type Props = { + value: string; + className?: string; + onChange?: (value: string) => void; +}; + +export const HashtagSelect = ({ + className = "", + onChange = () => {}, + value, +}: Props) => ( + void; }; -const TextArea: React.FC = ({ +const TextArea = ({ id, label, value = undefined, @@ -534,7 +529,7 @@ const TextArea: React.FC = ({ disabled = false, rows = 3, onChange = (_) => {}, -}) => { +}: TextAreaProps) => { return (