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.
807 lines
22 KiB
Vue
807 lines
22 KiB
Vue
<template>
|
|
<div class="profile-page">
|
|
<!-- Loading State -->
|
|
<div v-if="loading" class="loading-state">
|
|
<p style="color: var(--text-faint)">Loading your profile...</p>
|
|
</div>
|
|
|
|
<!-- Unauthenticated State -->
|
|
<div v-else-if="!memberData" class="loading-state">
|
|
<p style="color: var(--text-faint); margin-bottom: 12px">
|
|
Please sign in to access your profile settings.
|
|
</p>
|
|
<button
|
|
class="btn btn-primary"
|
|
@click="
|
|
openLoginModal({
|
|
title: 'Sign in to your profile',
|
|
description: 'Enter your email to manage your profile settings',
|
|
})
|
|
"
|
|
>
|
|
Sign In
|
|
</button>
|
|
</div>
|
|
|
|
<div v-else class="profile-authenticated">
|
|
<!-- PAGE HEADER -->
|
|
<PageHeader
|
|
title="Edit Profile"
|
|
subtitle="How you appear to other members"
|
|
>
|
|
<NuxtLink
|
|
v-if="
|
|
(memberData?._id || memberData?.id) &&
|
|
memberData?.status === 'active' &&
|
|
formData.showInDirectory
|
|
"
|
|
:to="`/members/${memberData?._id || memberData?.id}`"
|
|
class="view-profile-link"
|
|
>
|
|
View my public profile →
|
|
</NuxtLink>
|
|
</PageHeader>
|
|
|
|
<!-- TWO-COLUMN FORM -->
|
|
<form class="page-content" @submit.prevent="handleSubmit">
|
|
<div class="profile-main">
|
|
<div class="profile-columns">
|
|
<!-- ======== LEFT COLUMN ======== -->
|
|
<div class="profile-col-left">
|
|
<div class="profile-col-inset">
|
|
<div class="section-label">Basics</div>
|
|
|
|
<div class="field">
|
|
<label>Name</label>
|
|
<input
|
|
v-model="formData.name"
|
|
type="text"
|
|
placeholder="Your name"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div class="row-2">
|
|
<div class="field">
|
|
<label>Pronouns</label>
|
|
<input
|
|
v-model="formData.pronouns"
|
|
type="text"
|
|
placeholder="e.g., she/her, they/them"
|
|
/>
|
|
<PrivacyToggle v-model="formData.pronounsPrivacy" />
|
|
</div>
|
|
<div class="field">
|
|
<label>Timezone</label>
|
|
<input
|
|
v-model="formData.timeZone"
|
|
type="text"
|
|
placeholder="e.g., America/Toronto"
|
|
/>
|
|
<PrivacyToggle v-model="formData.timeZonePrivacy" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Avatar</label>
|
|
<div class="avatar-row">
|
|
<button
|
|
v-for="ghost in availableGhosts"
|
|
:key="ghost.value"
|
|
type="button"
|
|
class="avatar-option"
|
|
:class="{ selected: formData.avatar === ghost.value }"
|
|
:title="ghost.label"
|
|
@click="formData.avatar = ghost.value"
|
|
>
|
|
<img :src="ghost.image" :alt="ghost.label" />
|
|
</button>
|
|
</div>
|
|
<PrivacyToggle v-model="formData.avatarPrivacy" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- About You -->
|
|
<hr class="section-divider" />
|
|
<div class="profile-col-inset">
|
|
<div class="section-label">About You</div>
|
|
|
|
<div class="row-2">
|
|
<div class="field">
|
|
<label>Studio / Organization</label>
|
|
<input
|
|
v-model="formData.studio"
|
|
type="text"
|
|
placeholder="Studio name"
|
|
/>
|
|
<PrivacyToggle v-model="formData.studioPrivacy" />
|
|
</div>
|
|
<div class="field">
|
|
<label>Location</label>
|
|
<input
|
|
v-model="formData.location"
|
|
type="text"
|
|
placeholder="Toronto, ON"
|
|
/>
|
|
<PrivacyToggle v-model="formData.locationPrivacy" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Bio</label>
|
|
<textarea
|
|
v-model="formData.bio"
|
|
rows="4"
|
|
placeholder="Share your background, interests, and experience..."
|
|
maxlength="300"
|
|
></textarea>
|
|
<div class="char-count">
|
|
{{ formData.bio?.length || 0 }} / 300
|
|
</div>
|
|
<PrivacyToggle v-model="formData.bioPrivacy" />
|
|
</div>
|
|
|
|
<!-- What I Do (craft tags) -->
|
|
<div class="field">
|
|
<label>What I Do</label>
|
|
<CraftTagSelector
|
|
v-model="formData.craftTags"
|
|
:tags="craftTags"
|
|
@suggest="tagSuggestPool = 'craft'; showTagSuggestModal = true"
|
|
/>
|
|
<PrivacyToggle v-model="formData.craftTagsPrivacy" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Visibility -->
|
|
<hr class="section-divider" />
|
|
<div class="profile-col-inset">
|
|
<div class="section-label">Visibility</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.showInDirectory" />
|
|
<div class="toggle-label">
|
|
Show in Member Directory
|
|
<span class="toggle-sub"
|
|
>Your profile will appear in the public member
|
|
listing</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ======== RIGHT COLUMN ======== -->
|
|
<div class="profile-col-right">
|
|
<div class="profile-col-inset">
|
|
<div class="section-label">Community Connections</div>
|
|
|
|
<div class="field">
|
|
<label>Topics</label>
|
|
<CooperativeTagSelector
|
|
v-model="formData.communityConnectionsTopics"
|
|
:tags="cooperativeTags"
|
|
@suggest="tagSuggestPool = 'cooperative'; showTagSuggestModal = true"
|
|
/>
|
|
<PrivacyToggle v-model="formData.communityConnectionsPrivacy" />
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Details</label>
|
|
<textarea
|
|
v-model="formData.communityConnectionsDetails"
|
|
rows="3"
|
|
placeholder="What are you hoping to connect about?"
|
|
maxlength="300"
|
|
></textarea>
|
|
<div class="char-count">
|
|
{{ formData.communityConnectionsDetails?.length || 0 }} / 300
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.communityConnectionsOfferPeerSupport" />
|
|
<div class="toggle-label">
|
|
Offer Peer Support
|
|
<span class="toggle-sub"
|
|
>Let other members request 1:1 time with you</span
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="formData.communityConnectionsOfferPeerSupport" class="connections-panel">
|
|
<div class="field">
|
|
<label>Availability</label>
|
|
<textarea
|
|
v-model="formData.communityConnectionsAvailability"
|
|
rows="3"
|
|
placeholder="e.g. Weekday afternoons ET"
|
|
></textarea>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Slack Handle</label>
|
|
<input
|
|
v-model="formData.communityConnectionsSlackHandle"
|
|
type="text"
|
|
placeholder="@yourslackname"
|
|
/>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label>Personal Message</label>
|
|
<textarea
|
|
v-model="formData.communityConnectionsPersonalMessage"
|
|
rows="3"
|
|
maxlength="200"
|
|
placeholder="Brief note shown to people requesting time with you"
|
|
></textarea>
|
|
<div class="char-count">
|
|
{{ formData.communityConnectionsPersonalMessage?.length || 0 }} / 200
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notifications -->
|
|
<hr class="section-divider" />
|
|
<div class="profile-col-inset">
|
|
<div class="section-label">Notifications</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.notifyEvents" />
|
|
<div class="toggle-label">
|
|
Event reminders
|
|
<span class="toggle-sub"
|
|
>Get notified about upcoming events</span
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.notifyUpdates" />
|
|
<div class="toggle-label">
|
|
Community updates
|
|
<span class="toggle-sub"
|
|
>New posts from members you follow</span
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toggle-field">
|
|
<USwitch v-model="formData.notifyConnectionRequests" />
|
|
<div class="toggle-label">
|
|
Connection requests
|
|
<span class="toggle-sub"
|
|
>When someone wants to connect</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ======== SAVE BAR ======== -->
|
|
<div class="save-bar">
|
|
<button
|
|
type="submit"
|
|
class="btn btn-primary"
|
|
:disabled="saving || !hasChanges"
|
|
>
|
|
{{ saving ? "Saving..." : "Save Profile" }}
|
|
</button>
|
|
<button type="button" class="btn" @click="resetForm">
|
|
Reset Changes
|
|
</button>
|
|
<span v-if="saveSuccess" class="save-msg save-msg-ok"
|
|
>Profile updated.</span
|
|
>
|
|
<span v-if="saveError" class="save-msg save-msg-err">{{
|
|
saveError
|
|
}}</span>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Tag Suggest Modal -->
|
|
<TagSuggestModal v-model:open="showTagSuggestModal" :pool="tagSuggestPool" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const { memberData, checkMemberStatus } = useAuth();
|
|
const { openLoginModal } = useLoginModal();
|
|
|
|
// Available ghost avatars
|
|
const availableGhosts = [
|
|
{
|
|
value: "disbelieving",
|
|
label: "Disbelieving",
|
|
image: "/ghosties/Ghost-Disbelieving.png",
|
|
},
|
|
{
|
|
value: "double-take",
|
|
label: "Double Take",
|
|
image: "/ghosties/Ghost-Double-Take.png",
|
|
},
|
|
{
|
|
value: "exasperated",
|
|
label: "Exasperated",
|
|
image: "/ghosties/Ghost-Exasperated.png",
|
|
},
|
|
{ value: "mild", label: "Mild", image: "/ghosties/Ghost-Mild.png" },
|
|
{ value: "sweet", label: "Sweet", image: "/ghosties/Ghost-Sweet.png" },
|
|
{ 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: "",
|
|
pronouns: "",
|
|
timeZone: "",
|
|
avatar: "",
|
|
studio: "",
|
|
bio: "",
|
|
location: "",
|
|
showInDirectory: true,
|
|
// Craft tags
|
|
craftTags: [],
|
|
craftTagsPrivacy: "members",
|
|
// Community connections
|
|
communityConnectionsTopics: [],
|
|
communityConnectionsPrivacy: "members",
|
|
communityConnectionsDetails: "",
|
|
communityConnectionsOfferPeerSupport: false,
|
|
communityConnectionsAvailability: "",
|
|
communityConnectionsSlackHandle: "",
|
|
communityConnectionsPersonalMessage: "",
|
|
// Privacy
|
|
pronounsPrivacy: "members",
|
|
timeZonePrivacy: "members",
|
|
avatarPrivacy: "members",
|
|
studioPrivacy: "members",
|
|
bioPrivacy: "members",
|
|
locationPrivacy: "members",
|
|
// Notifications
|
|
notifyEvents: true,
|
|
notifyUpdates: true,
|
|
notifyPeerRequests: true,
|
|
notifyConnectionRequests: true,
|
|
});
|
|
|
|
const loading = ref(false);
|
|
const saving = ref(false);
|
|
const saveSuccess = ref(false);
|
|
const saveError = ref(null);
|
|
const initialData = ref(null);
|
|
|
|
// Computed
|
|
const hasChanges = computed(() => {
|
|
return JSON.stringify(formData) !== JSON.stringify(initialData.value);
|
|
});
|
|
|
|
// Load member data into form
|
|
const loadProfile = () => {
|
|
if (memberData.value) {
|
|
formData.name = memberData.value.name || "";
|
|
formData.pronouns = memberData.value.pronouns || "";
|
|
formData.timeZone = memberData.value.timeZone || "";
|
|
formData.avatar = memberData.value.avatar || "";
|
|
formData.studio = memberData.value.studio || "";
|
|
formData.bio = memberData.value.bio || "";
|
|
formData.location = memberData.value.location || "";
|
|
|
|
formData.showInDirectory = memberData.value.showInDirectory ?? true;
|
|
|
|
// 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 || {};
|
|
formData.pronounsPrivacy = privacy.pronouns || "members";
|
|
formData.timeZonePrivacy = privacy.timeZone || "members";
|
|
formData.avatarPrivacy = privacy.avatar || "members";
|
|
formData.studioPrivacy = privacy.studio || "members";
|
|
formData.bioPrivacy = privacy.bio || "members";
|
|
formData.locationPrivacy = privacy.location || "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));
|
|
}
|
|
};
|
|
|
|
// Handle form submission
|
|
const handleSubmit = async () => {
|
|
saving.value = true;
|
|
saveSuccess.value = false;
|
|
saveError.value = null;
|
|
|
|
try {
|
|
// 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 community connections data
|
|
await $fetch("/api/members/me/community-connections", {
|
|
method: "PATCH",
|
|
body: {
|
|
topics: formData.communityConnectionsTopics,
|
|
offerPeerSupport: formData.communityConnectionsOfferPeerSupport,
|
|
availability: formData.communityConnectionsAvailability,
|
|
slackHandle: formData.communityConnectionsSlackHandle,
|
|
personalMessage: formData.communityConnectionsPersonalMessage,
|
|
details: formData.communityConnectionsDetails,
|
|
},
|
|
});
|
|
|
|
saveSuccess.value = true;
|
|
|
|
// Refresh member data
|
|
await checkMemberStatus();
|
|
loadProfile();
|
|
|
|
setTimeout(() => {
|
|
saveSuccess.value = false;
|
|
}, 3000);
|
|
} catch (error) {
|
|
console.error("Profile save error:", error);
|
|
saveError.value =
|
|
error.data?.message || "Failed to save profile. Please try again.";
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
};
|
|
|
|
// Reset form to initial state
|
|
const resetForm = () => {
|
|
loadProfile();
|
|
saveSuccess.value = false;
|
|
saveError.value = null;
|
|
};
|
|
|
|
// Initialize on mount
|
|
onMounted(async () => {
|
|
if (!memberData.value) {
|
|
loading.value = true;
|
|
const isAuthenticated = await checkMemberStatus();
|
|
loading.value = false;
|
|
|
|
if (!isAuthenticated) {
|
|
openLoginModal({
|
|
title: "Sign in to your profile",
|
|
description: "Enter your email to manage your profile settings",
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
loadProfile();
|
|
});
|
|
|
|
useHead({
|
|
title: "Edit Profile - Ghost Guild",
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.profile-page {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.profile-authenticated {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
/* ---- LOADING / EMPTY STATE ---- */
|
|
.loading-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 80px 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.profile-page > .loading-state {
|
|
flex: 1;
|
|
}
|
|
|
|
/* ---- CONTENT AREA ---- */
|
|
.page-content {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.profile-main {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
/* ---- TWO-COLUMN LAYOUT ---- */
|
|
.profile-columns {
|
|
flex: 1;
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
grid-template-rows: 1fr;
|
|
gap: 0;
|
|
align-items: stretch;
|
|
min-height: 0;
|
|
}
|
|
|
|
.profile-col-left,
|
|
.profile-col-right {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
align-self: stretch;
|
|
}
|
|
|
|
@media (min-width: 1025px) {
|
|
.profile-col-left {
|
|
border-right: 1px dashed var(--border);
|
|
}
|
|
}
|
|
|
|
.profile-col-left > .profile-col-inset:first-of-type,
|
|
.profile-col-right > .profile-col-inset:first-of-type {
|
|
padding-top: 14px;
|
|
}
|
|
|
|
.profile-col-left .profile-col-inset {
|
|
padding-left: 28px;
|
|
padding-right: 24px;
|
|
}
|
|
|
|
.profile-col-right .profile-col-inset {
|
|
padding-left: 24px;
|
|
padding-right: 28px;
|
|
}
|
|
|
|
/* ---- MULTI-COLUMN ROWS ---- */
|
|
.row-2 {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 14px;
|
|
}
|
|
|
|
/* ---- PRIVACY TOGGLE SPACING ---- */
|
|
.field :deep(.priv) {
|
|
margin-top: 4px;
|
|
}
|
|
|
|
/* ---- FIELD LABELS (distinct from .section-label) ---- */
|
|
.field label {
|
|
font-size: 11px;
|
|
text-transform: none;
|
|
letter-spacing: normal;
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
/* ---- SOLID INPUT BORDERS ---- */
|
|
.field input,
|
|
.field select,
|
|
.field textarea {
|
|
border-style: solid;
|
|
}
|
|
|
|
.field :deep(.tags) {
|
|
border-style: solid;
|
|
}
|
|
|
|
/* ---- VIEW PROFILE LINK ---- */
|
|
.view-profile-link {
|
|
display: inline-block;
|
|
margin-top: 8px;
|
|
font-size: 12px;
|
|
color: var(--candle);
|
|
text-decoration: none;
|
|
}
|
|
.view-profile-link:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* ---- AVATAR PICKER ---- */
|
|
.avatar-row {
|
|
display: flex;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
|
|
.avatar-option {
|
|
width: 40px;
|
|
height: 40px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px dashed var(--border);
|
|
background: var(--bg);
|
|
cursor: pointer;
|
|
padding: 3px;
|
|
transition: all 0.12s;
|
|
}
|
|
|
|
.avatar-option img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.avatar-option:hover {
|
|
border-color: var(--candle-dim);
|
|
background: var(--surface-hover);
|
|
}
|
|
|
|
.avatar-option.selected {
|
|
border: 2px solid var(--candle);
|
|
background: var(--surface);
|
|
}
|
|
|
|
/* ---- TEXTAREA RESIZE ---- */
|
|
.field textarea {
|
|
resize: vertical;
|
|
}
|
|
|
|
/* ---- CHAR COUNT ---- */
|
|
.char-count {
|
|
font-size: 10px;
|
|
color: var(--text-faint);
|
|
text-align: right;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
/* ---- TOGGLE FIELD ---- */
|
|
.toggle-field {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.toggle-label {
|
|
font-size: 12px;
|
|
color: var(--text);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.toggle-sub {
|
|
display: block;
|
|
font-size: 11px;
|
|
color: var(--text-faint);
|
|
margin-top: 1px;
|
|
}
|
|
|
|
/* ---- CONNECTIONS PANEL ---- */
|
|
.connections-panel {
|
|
border: 1px dashed var(--border);
|
|
padding: 12px 14px;
|
|
margin-top: 4px;
|
|
margin-bottom: 12px;
|
|
background: var(--surface);
|
|
}
|
|
|
|
/* ---- DISABLED BUTTON ---- */
|
|
.btn:disabled {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* ---- SAVE BAR ---- */
|
|
.save-bar {
|
|
flex-shrink: 0;
|
|
padding: 24px 28px 24px;
|
|
margin-top: 0;
|
|
border-top: 1px dashed var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.save-msg {
|
|
font-size: 11px;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.save-msg-ok {
|
|
color: var(--green, var(--candle));
|
|
}
|
|
|
|
.save-msg-err {
|
|
color: var(--ember);
|
|
}
|
|
|
|
/* ---- RESPONSIVE ---- */
|
|
@media (max-width: 1024px) {
|
|
.profile-columns {
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: auto;
|
|
}
|
|
|
|
.profile-col-left {
|
|
border-right: none;
|
|
border-bottom: 1px dashed var(--border);
|
|
padding-bottom: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.profile-col-left .profile-col-inset,
|
|
.profile-col-right .profile-col-inset {
|
|
padding-left: 28px;
|
|
padding-right: 28px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.row-2 {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.profile-col-left .profile-col-inset,
|
|
.profile-col-right .profile-col-inset {
|
|
padding-left: 16px;
|
|
padding-right: 16px;
|
|
}
|
|
|
|
.save-bar {
|
|
padding-left: 16px;
|
|
padding-right: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
</style>
|