Add Markdown support and update member features

The commit adds Markdown rendering capabilities and makes several UI/UX
improvements across member-related features including profile display,
peer support badges, and navigation structure.

Includes:
- Added @tailwindcss/typography plugin
- New Markdown rendering composable
- Simplified member navigation links
- Enhanced member profile layout and styling
- Added peer support badge component
- Improved mobile responsiveness
- Removed redundant icons and simplified UI
This commit is contained in:
Jennie Robinson Faber 2025-10-07 15:07:27 +01:00
parent fb02688166
commit 1f7a0f40c0
11 changed files with 375 additions and 432 deletions

View file

@ -99,9 +99,6 @@
block
title="Coming soon"
>
<template #leading>
<Icon name="heroicons:calendar-days" class="w-5 h-5" />
</template>
Propose an Event
</UButton>
@ -112,9 +109,6 @@
block
title="Coming soon"
>
<template #leading>
<Icon name="heroicons:user-group" class="w-5 h-5" />
</template>
Book a Peer Session
</UButton>
@ -125,9 +119,6 @@
block
title="Coming soon"
>
<template #leading>
<Icon name="heroicons:book-open" class="w-5 h-5" />
</template>
Browse Resources
</UButton>
@ -137,130 +128,38 @@
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500 justify-start"
block
>
<template #leading>
<Icon name="heroicons:user-circle" class="w-5 h-5" />
</template>
Update Profile
</UButton>
<UButton
to="/events"
variant="outline"
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500 justify-start"
block
>
View Events
</UButton>
<UButton
to="/members"
variant="outline"
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500 justify-start"
block
>
Browse Members
</UButton>
<UButton
to="/member/profile#account"
variant="outline"
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500 justify-start"
block
>
Manage Account
</UButton>
</div>
</UCard>
<!-- Quick Actions Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<UCard
:ui="{
root: 'bg-ghost-900 border border-ghost-700 hover:border-whisper-600 transition-colors',
header: 'border-b-0',
body: 'bg-ghost-900',
footer: 'border-t-0 bg-ghost-900',
}"
class="hover:border-whisper-600 transition-colors"
>
<template #header>
<div
class="w-12 h-12 bg-ghost-800 border border-ghost-600 flex items-center justify-center"
>
<Icon
name="heroicons:calendar-days"
class="w-6 h-6 text-whisper-400"
/>
</div>
</template>
<h3 class="text-lg font-semibold mb-2 text-ghost-100">
Upcoming Events
</h3>
<p class="text-ghost-300 mb-4">
Discover and register for community events and workshops.
</p>
<template #footer>
<UButton
to="/events"
variant="outline"
size="sm"
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500"
>
View Events
</UButton>
</template>
</UCard>
<UCard
:ui="{
root: 'bg-ghost-900 border border-ghost-700 hover:border-whisper-600 transition-colors',
header: 'border-b-0',
body: 'bg-ghost-900',
footer: 'border-t-0 bg-ghost-900',
}"
>
<template #header>
<div
class="w-12 h-12 bg-ghost-800 border border-ghost-600 flex items-center justify-center"
>
<Icon
name="heroicons:user-group"
class="w-6 h-6 text-whisper-400"
/>
</div>
</template>
<h3 class="text-lg font-semibold mb-2 text-ghost-100">Community</h3>
<p class="text-ghost-300 mb-4">
Connect with other members in your circle and beyond.
</p>
<template #footer>
<UButton
to="/members"
variant="outline"
size="sm"
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500"
>
Browse Members
</UButton>
</template>
</UCard>
<UCard
:ui="{
root: 'bg-ghost-900 border border-ghost-700 hover:border-whisper-600 transition-colors',
header: 'border-b-0',
body: 'bg-ghost-900',
footer: 'border-t-0 bg-ghost-900',
}"
>
<template #header>
<div
class="w-12 h-12 bg-ghost-800 border border-ghost-600 flex items-center justify-center"
>
<Icon
name="heroicons:cog-6-tooth"
class="w-6 h-6 text-whisper-400"
/>
</div>
</template>
<h3 class="text-lg font-semibold mb-2 text-ghost-100">
Account Settings
</h3>
<p class="text-ghost-300 mb-4">
Manage your profile and membership settings.
</p>
<template #footer>
<UButton
to="/member/profile#account"
variant="outline"
size="sm"
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500"
>
Manage Account
</UButton>
</template>
</UCard>
</div>
<!-- Your Registered Events -->
<UCard
:ui="{

View file

@ -429,28 +429,12 @@
<UFormField
label="Conversational Support Topics"
name="peerSupportSupportTopics"
description="Select emotional and conversational support areas"
>
<div class="space-y-2 mt-2">
<label
v-for="topic in availableSupportTopics"
:key="topic"
class="flex items-center gap-3 p-3 rounded-lg border border-ghost-700 hover:border-purple-500/50 transition-colors cursor-pointer"
:class="
formData.peerSupportSupportTopics.includes(topic)
? 'bg-purple-500/10 border-purple-500/50'
: 'bg-ghost-900/50'
"
>
<input
type="checkbox"
:value="topic"
v-model="formData.peerSupportSupportTopics"
class="rounded border-ghost-600 text-purple-500 focus:ring-purple-500 focus:ring-offset-0 bg-ghost-800"
/>
<span class="text-ghost-200">{{ topic }}</span>
</label>
</div>
<UCheckboxGroup
v-model="formData.peerSupportSupportTopics"
:items="availableSupportTopics"
color="primary"
/>
</UFormField>
<!-- Availability -->
@ -927,6 +911,9 @@ const hasChanges = computed(() => {
// Offering tag management
const addOfferingTag = () => {
const tag = currentOfferingTagInput.value.trim().replace(/,$/, "");
if (!Array.isArray(formData.offering.tags)) {
formData.offering.tags = [];
}
if (tag && !formData.offering.tags.includes(tag)) {
formData.offering.tags.push(tag);
currentOfferingTagInput.value = "";
@ -934,12 +921,16 @@ const addOfferingTag = () => {
};
const removeOfferingTag = (index) => {
if (!formData.offering.tags) return;
formData.offering.tags.splice(index, 1);
};
// Looking For tag management
const addLookingForTag = () => {
const tag = currentLookingForTagInput.value.trim().replace(/,$/, "");
if (!Array.isArray(formData.lookingFor.tags)) {
formData.lookingFor.tags = [];
}
if (tag && !formData.lookingFor.tags.includes(tag)) {
formData.lookingFor.tags.push(tag);
currentLookingForTagInput.value = "";
@ -947,6 +938,7 @@ const addLookingForTag = () => {
};
const removeLookingForTag = (index) => {
if (!formData.lookingFor.tags) return;
formData.lookingFor.tags.splice(index, 1);
};
@ -964,6 +956,9 @@ const removePeerSkillTopic = (index) => {
};
const addSuggestedSkillTopic = (tag) => {
if (!Array.isArray(formData.peerSupportSkillTopics)) {
formData.peerSupportSkillTopics = [];
}
if (!formData.peerSupportSkillTopics.includes(tag)) {
formData.peerSupportSkillTopics.push(tag);
}
@ -982,22 +977,32 @@ const loadProfile = () => {
// Load offering (handle both old string and new object format)
if (typeof memberData.value.offering === "string") {
formData.offering = { text: memberData.value.offering, tags: [] };
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: memberData.value.offering?.text || "",
tags: memberData.value.offering?.tags || [],
};
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, tags: [] };
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: memberData.value.lookingFor?.text || "",
tags: memberData.value.lookingFor?.tags || [],
};
formData.lookingFor.text = "";
formData.lookingFor.tags = [];
}
formData.showInDirectory = memberData.value.showInDirectory ?? true;
@ -1006,16 +1011,29 @@ const loadProfile = () => {
if (memberData.value.peerSupport) {
formData.peerSupportEnabled =
memberData.value.peerSupport.enabled || false;
formData.peerSupportSkillTopics =
memberData.value.peerSupport.skillTopics || [];
formData.peerSupportSupportTopics =
memberData.value.peerSupport.supportTopics || [];
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 privacy settings (with defaults)