From 2aa29ba64bde192421585614cabb32c7a143826b Mon Sep 17 00:00:00 2001 From: Jennie Robinson Faber Date: Sun, 5 Apr 2026 16:28:34 +0100 Subject: [PATCH] feat: restructure profile page for community connections Replace Skills Exchange section with CraftTagSelector in About You. Replace Peer Support section with Community Connections using CooperativeTagSelector. Update form data, load/save logic, and notifications to use new field names with backward-compatible fallbacks to old peerSupport data. --- app/pages/member/profile.vue | 353 ++++++++++------------------------- 1 file changed, 97 insertions(+), 256 deletions(-) diff --git a/app/pages/member/profile.vue b/app/pages/member/profile.vue index e141981..0e861cc 100644 --- a/app/pages/member/profile.vue +++ b/app/pages/member/profile.vue @@ -140,45 +140,16 @@ - - - -
-
- +
- - What I Do + - -
-
- - -
- -
- - - -
-
- - +
@@ -203,10 +174,33 @@
- + + +
+ + + +
+ +
+ + +
+ {{ formData.communityConnectionsDetails?.length || 0 }} / 300 +
+
- +
Offer Peer Support
-
-
- - -
- Suggested from your offerings: - {{ tag }} -
-
- -
- -
- -
-
- +
@@ -264,7 +222,7 @@
@@ -273,13 +231,13 @@
- {{ formData.peerSupportMessage?.length || 0 }} / 200 + {{ formData.communityConnectionsPersonalMessage?.length || 0 }} / 200
@@ -311,9 +269,9 @@
- +
- Peer support requests + Connection requests When someone wants to connect @@ -345,6 +303,9 @@
+ + +
@@ -374,6 +335,20 @@ const availableGhosts = [ { value: "wtf", label: "WTF", image: "/ghosties/Ghost-WTF.png" }, ]; +// Fetch tags and split into pools +const { data: tagsData } = await useFetch("/api/tags"); + +const craftTags = computed(() => + (tagsData.value?.tags || []).filter((t) => t.pool === "craft"), +); +const cooperativeTags = computed(() => + (tagsData.value?.tags || []).filter((t) => t.pool === "cooperative"), +); + +// Tag suggest modal state +const showTagSuggestModal = ref(false); +const tagSuggestPool = ref(""); + // Form state const formData = reactive({ name: "", @@ -383,16 +358,18 @@ const formData = reactive({ studio: "", bio: "", location: "", - offering: { text: "", tags: [] }, - lookingFor: { text: "", tags: [] }, showInDirectory: true, - // Peer support - peerSupportEnabled: false, - peerSupportSkillTopics: [], - peerSupportSupportTopics: [], - peerSupportAvailability: "", - peerSupportMessage: "", - peerSupportSlackUsername: "", + // Craft tags + craftTags: [], + craftTagsPrivacy: "members", + // Community connections + communityConnectionsTopics: [], + communityConnectionsPrivacy: "members", + communityConnectionsDetails: "", + communityConnectionsOfferPeerSupport: false, + communityConnectionsAvailability: "", + communityConnectionsSlackHandle: "", + communityConnectionsPersonalMessage: "", // Privacy pronounsPrivacy: "members", timeZonePrivacy: "members", @@ -400,12 +377,11 @@ const formData = reactive({ studioPrivacy: "members", bioPrivacy: "members", locationPrivacy: "members", - offeringPrivacy: "members", - lookingForPrivacy: "members", // Notifications notifyEvents: true, notifyUpdates: true, notifyPeerRequests: true, + notifyConnectionRequests: true, }); const loading = ref(false); @@ -414,47 +390,11 @@ const saveSuccess = ref(false); const saveError = ref(null); const initialData = ref(null); -// Available conversational support topics -const availableSupportTopics = [ - "Co-founder relationships", - "Burnout prevention", - "Impostor syndrome", - "Work-life boundaries", - "Conflict resolution", - "General chat & support", -]; - // Computed const hasChanges = computed(() => { return JSON.stringify(formData) !== JSON.stringify(initialData.value); }); -const suggestedSkillTopics = computed(() => { - if (!formData.offering.tags?.length) return []; - return formData.offering.tags.filter( - (t) => !formData.peerSupportSkillTopics?.includes(t), - ); -}); - -// Toggle a support topic in/out of the selection -const toggleSupportTopic = (topic) => { - const idx = formData.peerSupportSupportTopics.indexOf(topic); - if (idx >= 0) { - formData.peerSupportSupportTopics.splice(idx, 1); - } else { - formData.peerSupportSupportTopics.push(topic); - } -}; - -const addSuggestedSkillTopic = (tag) => { - if (!Array.isArray(formData.peerSupportSkillTopics)) { - formData.peerSupportSkillTopics = []; - } - if (!formData.peerSupportSkillTopics.includes(tag)) { - formData.peerSupportSkillTopics.push(tag); - } -}; - // Load member data into form const loadProfile = () => { if (memberData.value) { @@ -466,66 +406,21 @@ const loadProfile = () => { formData.bio = memberData.value.bio || ""; formData.location = memberData.value.location || ""; - // Load offering (handle both old string and new object format) - if (typeof memberData.value.offering === "string") { - formData.offering.text = memberData.value.offering; - formData.offering.tags = []; - } else if (memberData.value.offering) { - formData.offering.text = memberData.value.offering?.text || ""; - formData.offering.tags = Array.isArray(memberData.value.offering?.tags) - ? [...memberData.value.offering.tags] - : []; - } else { - formData.offering.text = ""; - formData.offering.tags = []; - } - - // Load lookingFor (handle both old string and new object format) - if (typeof memberData.value.lookingFor === "string") { - formData.lookingFor.text = memberData.value.lookingFor; - formData.lookingFor.tags = []; - } else if (memberData.value.lookingFor) { - formData.lookingFor.text = memberData.value.lookingFor?.text || ""; - formData.lookingFor.tags = Array.isArray( - memberData.value.lookingFor?.tags, - ) - ? [...memberData.value.lookingFor.tags] - : []; - } else { - formData.lookingFor.text = ""; - formData.lookingFor.tags = []; - } - formData.showInDirectory = memberData.value.showInDirectory ?? true; - // Load peer support data - if (memberData.value.peerSupport) { - formData.peerSupportEnabled = - memberData.value.peerSupport.enabled || false; - formData.peerSupportSkillTopics = Array.isArray( - memberData.value.peerSupport.skillTopics, - ) - ? [...memberData.value.peerSupport.skillTopics] - : []; - formData.peerSupportSupportTopics = Array.isArray( - memberData.value.peerSupport.supportTopics, - ) - ? [...memberData.value.peerSupport.supportTopics] - : []; - formData.peerSupportAvailability = - memberData.value.peerSupport.availability || ""; - formData.peerSupportMessage = - memberData.value.peerSupport.personalMessage || ""; - formData.peerSupportSlackUsername = - memberData.value.peerSupport.slackUsername || ""; - } else { - formData.peerSupportEnabled = false; - formData.peerSupportSkillTopics = []; - formData.peerSupportSupportTopics = []; - formData.peerSupportAvailability = ""; - formData.peerSupportMessage = ""; - formData.peerSupportSlackUsername = ""; - } + // Load craft tags + formData.craftTags = Array.isArray(memberData.value.craftTags) + ? [...memberData.value.craftTags] + : []; + + // Load community connections (with fallback to old peerSupport fields) + const cc = memberData.value.communityConnections || {}; + formData.communityConnectionsTopics = Array.isArray(cc.topics) ? [...cc.topics] : []; + formData.communityConnectionsOfferPeerSupport = cc.offerPeerSupport ?? memberData.value.peerSupport?.enabled ?? false; + formData.communityConnectionsAvailability = cc.availability || memberData.value.peerSupport?.availability || ""; + formData.communityConnectionsSlackHandle = cc.slackHandle || memberData.value.peerSupport?.slackUsername || ""; + formData.communityConnectionsPersonalMessage = cc.personalMessage || memberData.value.peerSupport?.personalMessage || ""; + formData.communityConnectionsDetails = cc.details || ""; // Load privacy settings (with defaults) const privacy = memberData.value.privacy || {}; @@ -535,14 +430,15 @@ const loadProfile = () => { formData.studioPrivacy = privacy.studio || "members"; formData.bioPrivacy = privacy.bio || "members"; formData.locationPrivacy = privacy.location || "members"; - formData.offeringPrivacy = privacy.offering || "members"; - formData.lookingForPrivacy = privacy.lookingFor || "members"; + formData.craftTagsPrivacy = privacy.craftTags || "members"; + formData.communityConnectionsPrivacy = privacy.communityConnections || "members"; // Load notification prefs const notifs = memberData.value.notifications || {}; formData.notifyEvents = notifs.events ?? true; formData.notifyUpdates = notifs.updates ?? true; formData.notifyPeerRequests = notifs.peerRequests ?? true; + formData.notifyConnectionRequests = notifs.connectionRequests ?? true; // Store initial state for change detection initialData.value = JSON.parse(JSON.stringify(formData)); @@ -556,29 +452,31 @@ const handleSubmit = async () => { saveError.value = null; try { - // Save profile data + // Save profile data (includes craft tags + privacy + notifications) await $fetch("/api/members/profile", { method: "PATCH", body: { ...formData, + craftTags: formData.craftTags, notifications: { events: formData.notifyEvents, updates: formData.notifyUpdates, peerRequests: formData.notifyPeerRequests, + connectionRequests: formData.notifyConnectionRequests, }, }, }); - // Save peer support data separately - await $fetch("/api/members/me/peer-support", { + // Save community connections data + await $fetch("/api/members/me/community-connections", { method: "PATCH", body: { - enabled: formData.peerSupportEnabled, - skillTopics: formData.peerSupportSkillTopics, - supportTopics: formData.peerSupportSupportTopics, - availability: formData.peerSupportAvailability, - personalMessage: formData.peerSupportMessage, - slackUsername: formData.peerSupportSlackUsername, + topics: formData.communityConnectionsTopics, + offerPeerSupport: formData.communityConnectionsOfferPeerSupport, + availability: formData.communityConnectionsAvailability, + slackHandle: formData.communityConnectionsSlackHandle, + personalMessage: formData.communityConnectionsPersonalMessage, + details: formData.communityConnectionsDetails, }, }); @@ -829,48 +727,8 @@ useHead({ margin-top: 1px; } -/* ---- CHECKBOX GRID ---- */ -.checkbox-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 3px 12px; -} - -.checkbox-item { - display: flex; - align-items: center; - gap: 5px; - font-size: 11px; - color: var(--text-dim); - cursor: pointer; - padding: 2px 0; - user-select: none; -} - -.checkbox-item .cb { - width: 13px; - height: 13px; - border: 1px dashed var(--border); - display: flex; - align-items: center; - justify-content: center; - font-size: 9px; - color: transparent; - flex-shrink: 0; -} - -.checkbox-item.checked .cb { - border-color: var(--candle); - border-style: solid; - color: var(--candle); -} - -.checkbox-item:hover { - color: var(--text); -} - -/* ---- PEER SUPPORT PANEL ---- */ -.peer-panel { +/* ---- CONNECTIONS PANEL ---- */ +.connections-panel { border: 1px dashed var(--border); padding: 12px 14px; margin-top: 4px; @@ -878,19 +736,6 @@ useHead({ background: var(--surface); } -.peer-panel .suggested { - font-size: 10px; - color: var(--text-faint); - margin-top: 4px; -} - -.peer-panel .suggested a { - color: var(--candle); - text-decoration: underline; - cursor: pointer; - margin-left: 4px; -} - /* ---- DISABLED BUTTON ---- */ .btn:disabled { opacity: 0.4; @@ -947,10 +792,6 @@ useHead({ grid-template-columns: 1fr; } - .checkbox-grid { - grid-template-columns: 1fr; - } - .profile-col-left .profile-col-inset, .profile-col-right .profile-col-inset { padding-left: 16px;