Add light/dark mode support with CSS variables
This commit is contained in:
parent
970b185151
commit
fb02688166
25 changed files with 1293 additions and 1177 deletions
|
|
@ -7,7 +7,7 @@
|
|||
>
|
||||
<!-- Left: Copyright and minimal info -->
|
||||
<div>
|
||||
<p class="text-stone-500 text-xs mb-2">
|
||||
<p class="text-ghost-500 text-xs mb-2">
|
||||
© {{ currentYear }} Ghost Guild
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<div class="flex flex-wrap gap-6 text-xs">
|
||||
<a
|
||||
href="mailto:hello@ghostguild.org"
|
||||
class="text-stone-500 hover:text-stone-300 transition-colors"
|
||||
class="text-ghost-500 hover:text-ghost-300 transition-colors"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,27 +1,38 @@
|
|||
<template>
|
||||
<nav
|
||||
class="w-64 lg:w-80 backdrop-blur-sm h-screen sticky top-0 flex flex-col"
|
||||
:class="[
|
||||
isMobile
|
||||
? 'w-full flex flex-col bg-transparent'
|
||||
: 'w-64 lg:w-80 backdrop-blur-sm h-screen sticky top-0 flex flex-col bg-ghost-900 border-r border-ghost-700',
|
||||
]"
|
||||
>
|
||||
<!-- Logo/Brand at top -->
|
||||
<div class="p-8 border-b border-ghost-800 bg-blue-400">
|
||||
<!-- Logo/Brand at top (desktop only) -->
|
||||
<div v-if="!isMobile" class="p-8 border-b border-ghost-700 bg-primary-500">
|
||||
<NuxtLink to="/" class="flex flex-col items-center gap-3 group">
|
||||
<span
|
||||
class="text-xl font-bold text-stone-100 ethereal-text tracking-wider"
|
||||
<span class="text-xl font-bold text-white ethereal-text tracking-wider"
|
||||
>Ghost Guild Logo</span
|
||||
>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<!-- Vertical Navigation -->
|
||||
<div class="flex-1 p-8 overflow-y-auto">
|
||||
<ul class="space-y-6">
|
||||
<div
|
||||
:class="
|
||||
isMobile ? 'flex-1 p-6 overflow-y-auto' : 'flex-1 p-8 overflow-y-auto'
|
||||
"
|
||||
>
|
||||
<ul :class="isMobile ? 'space-y-4' : 'space-y-6'">
|
||||
<li v-for="item in navigationItems" :key="item.path">
|
||||
<NuxtLink :to="item.path" class="block group relative">
|
||||
<NuxtLink
|
||||
:to="item.path"
|
||||
class="block group relative"
|
||||
@click="handleNavigate"
|
||||
>
|
||||
<!-- Hover indicator -->
|
||||
|
||||
<span
|
||||
class="text-stone-200 hover:text-stone-100 transition-all duration-300 text-lg tracking-wide block py-2 hover:ethereal-text"
|
||||
active-class="text-stone-100 ethereal-text translate-x-2"
|
||||
class="text-ghost-200 hover:text-ghost-100 transition-all duration-300 text-lg tracking-wide block py-2 hover:ethereal-text"
|
||||
active-class="text-ghost-100 ethereal-text translate-x-2"
|
||||
>
|
||||
{{ item.label }}
|
||||
</span>
|
||||
|
|
@ -29,12 +40,24 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Color Mode Switcher -->
|
||||
<div class="mb-6">
|
||||
<UColorModeButton size="md" class="w-full" />
|
||||
</div>
|
||||
|
||||
<!-- Auth section -->
|
||||
<div class="mt-12 pt-8 border-t border-ghost-800/50">
|
||||
<div
|
||||
:class="
|
||||
isMobile
|
||||
? 'mt-8 pt-6 border-t border-ghost-800/50'
|
||||
: 'mt-12 pt-8 border-t border-ghost-800/50'
|
||||
"
|
||||
>
|
||||
<div v-if="isAuthenticated" class="space-y-4">
|
||||
<NuxtLink
|
||||
to="/member/dashboard"
|
||||
class="block text-stone-300 hover:text-stone-100 hover:ethereal-text transition-all duration-300 py-2"
|
||||
class="block text-ghost-300 hover:text-ghost-100 hover:ethereal-text transition-all duration-300 py-2"
|
||||
@click="handleNavigate"
|
||||
>
|
||||
<span class="block text-sm text-whisper-400 mb-1">{{
|
||||
memberData?.name || "Member"
|
||||
|
|
@ -42,14 +65,14 @@
|
|||
Dashboard
|
||||
</NuxtLink>
|
||||
<button
|
||||
@click="logout"
|
||||
class="text-stone-500 hover:text-stone-300 transition-all duration-300 text-sm"
|
||||
@click="handleLogout"
|
||||
class="text-ghost-500 hover:text-ghost-300 transition-all duration-300 text-sm"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="space-y-4">
|
||||
<p class="text-stone-400 text-sm mb-4">
|
||||
<p class="text-ghost-400 text-sm mb-4">
|
||||
Enter your email to receive a login link
|
||||
</p>
|
||||
|
||||
|
|
@ -97,8 +120,30 @@
|
|||
<script setup>
|
||||
import { reactive, ref, computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["navigate"]);
|
||||
|
||||
const { isAuthenticated, logout, memberData } = useAuth();
|
||||
|
||||
const handleNavigate = () => {
|
||||
if (props.isMobile) {
|
||||
emit("navigate");
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
if (props.isMobile) {
|
||||
emit("navigate");
|
||||
}
|
||||
};
|
||||
|
||||
const publicNavigationItems = [
|
||||
{ label: "Home", path: "/", accent: "entry point" },
|
||||
{ label: "About", path: "/about", accent: "who we are" },
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
<div class="space-y-4">
|
||||
<!-- Current Image Preview -->
|
||||
<div v-if="modelValue?.url" class="relative">
|
||||
<img
|
||||
:src="transformedImageUrl"
|
||||
<img
|
||||
:src="transformedImageUrl"
|
||||
:alt="modelValue.alt || 'Event image'"
|
||||
class="w-full h-48 object-cover rounded-lg border border-gray-300"
|
||||
class="w-full h-48 object-cover rounded-lg border border-neutral-200"
|
||||
@error="console.log('Image failed to load:', transformedImageUrl)"
|
||||
@load="console.log('Image loaded successfully:', transformedImageUrl)"
|
||||
/>
|
||||
<button
|
||||
<button
|
||||
@click="removeImage"
|
||||
type="button"
|
||||
class="absolute top-2 right-2 p-1 bg-red-500 text-white rounded-full hover:bg-red-600 transition-colors"
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Upload Area -->
|
||||
<div
|
||||
<div
|
||||
v-if="!modelValue?.url"
|
||||
class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-gray-400 transition-colors"
|
||||
@dragover.prevent="isDragging = true"
|
||||
|
|
@ -34,12 +34,12 @@
|
|||
@change="handleFileSelect"
|
||||
class="hidden"
|
||||
/>
|
||||
|
||||
|
||||
<div class="space-y-3">
|
||||
<Icon name="heroicons:photo" class="w-12 h-12 text-gray-400 mx-auto" />
|
||||
<div>
|
||||
<p class="text-gray-600">
|
||||
<button
|
||||
<button
|
||||
type="button"
|
||||
@click="$refs.fileInput.click()"
|
||||
class="text-blue-600 hover:text-blue-500 font-medium"
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
:value="modelValue.alt || ''"
|
||||
@input="updateAltText($event.target.value)"
|
||||
placeholder="Describe this image..."
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
class="w-full border border-neutral-200 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
<span class="text-gray-600">{{ uploadProgress }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
<div
|
||||
class="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||
:style="`width: ${uploadProgress}%`"
|
||||
/>
|
||||
|
|
@ -91,111 +91,113 @@
|
|||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
default: () => null,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const isDragging = ref(false)
|
||||
const isUploading = ref(false)
|
||||
const uploadProgress = ref(0)
|
||||
const errorMessage = ref('')
|
||||
const fileInput = ref()
|
||||
const isDragging = ref(false);
|
||||
const isUploading = ref(false);
|
||||
const uploadProgress = ref(0);
|
||||
const errorMessage = ref("");
|
||||
const fileInput = ref();
|
||||
|
||||
// Transform image URL for preview (smaller size)
|
||||
const transformedImageUrl = computed(() => {
|
||||
console.log('modelValue in computed:', props.modelValue)
|
||||
|
||||
console.log("modelValue in computed:", props.modelValue);
|
||||
|
||||
// If we have the direct URL, use it
|
||||
if (props.modelValue?.url) {
|
||||
console.log('Using direct URL:', props.modelValue.url)
|
||||
return props.modelValue.url
|
||||
console.log("Using direct URL:", props.modelValue.url);
|
||||
return props.modelValue.url;
|
||||
}
|
||||
|
||||
|
||||
// Otherwise try to construct from publicId
|
||||
if (props.modelValue?.publicId) {
|
||||
const config = useRuntimeConfig()
|
||||
const constructedUrl = `https://res.cloudinary.com/${config.public.cloudinaryCloudName}/image/upload/w_400,h_200,c_fill,f_auto,q_auto/${props.modelValue.publicId}`
|
||||
console.log('Constructed URL:', constructedUrl)
|
||||
return constructedUrl
|
||||
const config = useRuntimeConfig();
|
||||
const constructedUrl = `https://res.cloudinary.com/${config.public.cloudinaryCloudName}/image/upload/w_400,h_200,c_fill,f_auto,q_auto/${props.modelValue.publicId}`;
|
||||
console.log("Constructed URL:", constructedUrl);
|
||||
return constructedUrl;
|
||||
}
|
||||
|
||||
console.log('No URL or publicId found')
|
||||
return ''
|
||||
})
|
||||
|
||||
console.log("No URL or publicId found");
|
||||
return "";
|
||||
});
|
||||
|
||||
const handleFileSelect = (event) => {
|
||||
const file = event.target.files[0]
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
uploadFile(file)
|
||||
uploadFile(file);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = (event) => {
|
||||
isDragging.value = false
|
||||
const files = event.dataTransfer.files
|
||||
isDragging.value = false;
|
||||
const files = event.dataTransfer.files;
|
||||
if (files.length > 0) {
|
||||
uploadFile(files[0])
|
||||
uploadFile(files[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const uploadFile = async (file) => {
|
||||
// Validate file
|
||||
if (!file.type.startsWith('image/')) {
|
||||
errorMessage.value = 'Please select an image file'
|
||||
return
|
||||
if (!file.type.startsWith("image/")) {
|
||||
errorMessage.value = "Please select an image file";
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > 10 * 1024 * 1024) { // 10MB
|
||||
errorMessage.value = 'File size must be less than 10MB'
|
||||
return
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
// 10MB
|
||||
errorMessage.value = "File size must be less than 10MB";
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessage.value = ''
|
||||
isUploading.value = true
|
||||
uploadProgress.value = 0
|
||||
errorMessage.value = "";
|
||||
isUploading.value = true;
|
||||
uploadProgress.value = 0;
|
||||
|
||||
try {
|
||||
// Create form data for upload
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
// Upload to Cloudinary
|
||||
const response = await $fetch(`/api/upload/image`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
onUploadProgress: (progress) => {
|
||||
uploadProgress.value = Math.round((progress.loaded / progress.total) * 100)
|
||||
}
|
||||
})
|
||||
uploadProgress.value = Math.round(
|
||||
(progress.loaded / progress.total) * 100,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Upload response:', response)
|
||||
console.log("Upload response:", response);
|
||||
|
||||
// Update the model value
|
||||
emit('update:modelValue', {
|
||||
emit("update:modelValue", {
|
||||
url: response.secure_url,
|
||||
publicId: response.public_id,
|
||||
alt: ''
|
||||
})
|
||||
|
||||
alt: "",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Upload failed:', error)
|
||||
errorMessage.value = 'Upload failed. Please try again.'
|
||||
console.error("Upload failed:", error);
|
||||
errorMessage.value = "Upload failed. Please try again.";
|
||||
} finally {
|
||||
isUploading.value = false
|
||||
uploadProgress.value = 0
|
||||
isUploading.value = false;
|
||||
uploadProgress.value = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeImage = () => {
|
||||
emit('update:modelValue', null)
|
||||
}
|
||||
emit("update:modelValue", null);
|
||||
};
|
||||
|
||||
const updateAltText = (altText) => {
|
||||
emit('update:modelValue', {
|
||||
emit("update:modelValue", {
|
||||
...props.modelValue,
|
||||
alt: altText
|
||||
})
|
||||
}
|
||||
</script>
|
||||
alt: altText,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -28,21 +28,21 @@
|
|||
'rounded-2xl p-6 md:p-8 mb-12 backdrop-blur-sm',
|
||||
props.theme === 'ethereal'
|
||||
? 'bg-ghost-800/60 border border-ghost-700 ethereal-glow halftone-texture'
|
||||
: 'bg-white dark:bg-gray-800 shadow-xl border border-blue-200 dark:border-blue-800',
|
||||
: 'bg-[--ui-bg-elevated] shadow-xl border border-blue-200',
|
||||
]"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center justify-between gap-3 md:gap-4">
|
||||
<button
|
||||
:class="[
|
||||
'p-3 rounded-full transition-all duration-300',
|
||||
'p-2 md:p-3 rounded-full transition-all duration-300 flex-shrink-0',
|
||||
props.theme === 'ethereal'
|
||||
? 'bg-whisper-600/80 text-stone-100 hover:bg-whisper-500 ethereal-glow'
|
||||
? 'bg-whisper-600/80 text-ghost-100 hover:bg-whisper-500 ethereal-glow'
|
||||
: 'bg-blue-500 text-white hover:bg-blue-600',
|
||||
]"
|
||||
@click="$emit('prev')"
|
||||
>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
class="w-5 h-5 md:w-6 md:h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
@ -56,14 +56,14 @@
|
|||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="text-center flex-1">
|
||||
<div class="text-center flex-1 min-w-0">
|
||||
<slot name="interactive-content">
|
||||
<p
|
||||
:class="[
|
||||
'text-lg',
|
||||
'text-base md:text-lg',
|
||||
props.theme === 'ethereal'
|
||||
? 'text-stone-200'
|
||||
: 'text-gray-600 dark:text-gray-300',
|
||||
? 'text-ghost-200'
|
||||
: 'text-[--ui-text-muted]',
|
||||
]"
|
||||
>
|
||||
{{
|
||||
|
|
@ -76,15 +76,15 @@
|
|||
|
||||
<button
|
||||
:class="[
|
||||
'p-3 rounded-full transition-all duration-300',
|
||||
'p-2 md:p-3 rounded-full transition-all duration-300 flex-shrink-0',
|
||||
props.theme === 'ethereal'
|
||||
? 'bg-whisper-600/80 text-stone-100 hover:bg-whisper-500 ethereal-glow'
|
||||
? 'bg-whisper-600/80 text-ghost-100 hover:bg-whisper-500 ethereal-glow'
|
||||
: 'bg-blue-500 text-white hover:bg-blue-600',
|
||||
]"
|
||||
@click="$emit('next')"
|
||||
>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
class="w-5 h-5 md:w-6 md:h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
@ -178,12 +178,10 @@ defineEmits(["prev", "next"]);
|
|||
|
||||
const backgroundClass = computed(() => {
|
||||
const themes = {
|
||||
blue: "bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800",
|
||||
purple:
|
||||
"bg-gradient-to-br from-purple-50 to-violet-100 dark:from-gray-900 dark:to-purple-900/20",
|
||||
emerald:
|
||||
"bg-gradient-to-br from-emerald-50 to-teal-100 dark:from-gray-900 dark:to-emerald-900/20",
|
||||
gray: "bg-gray-50 dark:bg-gray-900",
|
||||
blue: "bg-gradient-to-br from-blue-50 to-indigo-100",
|
||||
purple: "bg-gradient-to-br from-purple-50 to-violet-100",
|
||||
emerald: "bg-gradient-to-br from-emerald-50 to-teal-100",
|
||||
gray: "bg-neutral-100",
|
||||
ethereal:
|
||||
"bg-gradient-to-br from-ghost-900 via-ghost-800 to-whisper-900 halftone-texture",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<span class="text-stone-300 font-medium">{{ label }}:</span>
|
||||
<span class="text-ghost-300 font-medium">{{ label }}:</span>
|
||||
<UButtonGroup size="sm" class="privacy-toggle-group">
|
||||
<UButton
|
||||
:variant="modelValue === 'members' ? 'solid' : 'outline'"
|
||||
|
|
@ -46,14 +46,14 @@ const updateValue = (value) => {
|
|||
<style scoped>
|
||||
/* Unselected buttons - lighter background for visibility */
|
||||
:deep(.privacy-toggle-btn:not(.is-selected)) {
|
||||
background-color: rgb(68 64 60) !important; /* stone-700 */
|
||||
border-color: rgb(87 83 78) !important; /* stone-600 */
|
||||
color: rgb(214 211 209) !important; /* stone-300 */
|
||||
background-color: rgb(68 64 60) !important; /* ghost-700 equivalent */
|
||||
border-color: rgb(87 83 78) !important; /* ghost-600 equivalent */
|
||||
color: rgb(214 211 209) !important; /* ghost-300 equivalent */
|
||||
}
|
||||
|
||||
:deep(.privacy-toggle-btn:not(.is-selected):hover) {
|
||||
background-color: rgb(87 83 78) !important; /* stone-600 */
|
||||
border-color: rgb(120 113 108) !important; /* stone-500 */
|
||||
background-color: rgb(87 83 78) !important; /* ghost-600 equivalent */
|
||||
border-color: rgb(120 113 108) !important; /* ghost-500 equivalent */
|
||||
}
|
||||
|
||||
/* Selected buttons - bright blue to stand out */
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
/>
|
||||
<div
|
||||
v-else
|
||||
class="w-12 h-12 rounded-full bg-stone-700 flex items-center justify-center text-stone-300 font-bold"
|
||||
class="w-12 h-12 rounded-full bg-ghost-700 flex items-center justify-center text-ghost-300 font-bold"
|
||||
>
|
||||
{{ update.author?.name?.charAt(0)?.toUpperCase() || "?" }}
|
||||
</div>
|
||||
|
|
@ -23,30 +23,30 @@
|
|||
<!-- Header -->
|
||||
<div class="flex items-start justify-between gap-4 mb-2">
|
||||
<div>
|
||||
<h3 class="font-semibold text-stone-100">
|
||||
<h3 class="font-semibold text-ghost-100">
|
||||
<NuxtLink
|
||||
v-if="update.author?._id"
|
||||
:to="`/updates/user/${update.author._id}`"
|
||||
class="hover:text-stone-300 transition-colors"
|
||||
class="hover:text-ghost-300 transition-colors"
|
||||
>
|
||||
{{ update.author.name }}
|
||||
</NuxtLink>
|
||||
<span v-else>Unknown Member</span>
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 text-sm text-stone-400">
|
||||
<div class="flex items-center gap-2 text-sm text-ghost-400">
|
||||
<time :datetime="update.createdAt">
|
||||
{{ formatDate(update.createdAt) }}
|
||||
</time>
|
||||
<span v-if="isEdited" class="text-stone-500">(edited)</span>
|
||||
<span v-if="isEdited" class="text-ghost-500">(edited)</span>
|
||||
<span
|
||||
v-if="update.privacy === 'private'"
|
||||
class="px-2 py-0.5 bg-stone-700 text-stone-300 rounded text-xs"
|
||||
class="px-2 py-0.5 bg-ghost-700 text-ghost-300 rounded text-xs"
|
||||
>
|
||||
Private
|
||||
</span>
|
||||
<span
|
||||
v-if="update.privacy === 'public'"
|
||||
class="px-2 py-0.5 bg-stone-700 text-stone-300 rounded text-xs"
|
||||
class="px-2 py-0.5 bg-ghost-700 text-ghost-300 rounded text-xs"
|
||||
>
|
||||
Public
|
||||
</span>
|
||||
|
|
@ -73,12 +73,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="text-stone-200 whitespace-pre-wrap break-words mb-3">
|
||||
<div class="text-ghost-200 whitespace-pre-wrap break-words mb-3">
|
||||
<template v-if="showPreview && update.content.length > 300">
|
||||
{{ update.content.substring(0, 300) }}...
|
||||
<NuxtLink
|
||||
:to="`/updates/${update._id}`"
|
||||
class="text-stone-400 hover:text-stone-300 ml-1"
|
||||
class="text-ghost-400 hover:text-ghost-300 ml-1"
|
||||
>
|
||||
Read more
|
||||
</NuxtLink>
|
||||
|
|
@ -100,14 +100,14 @@
|
|||
</div>
|
||||
|
||||
<!-- Footer actions -->
|
||||
<div class="flex items-center gap-4 text-sm text-stone-400">
|
||||
<div class="flex items-center gap-4 text-sm text-ghost-400">
|
||||
<NuxtLink
|
||||
:to="`/updates/${update._id}`"
|
||||
class="hover:text-stone-300 transition-colors"
|
||||
class="hover:text-ghost-300 transition-colors"
|
||||
>
|
||||
View full update
|
||||
</NuxtLink>
|
||||
<span v-if="update.commentsEnabled" class="text-stone-500">
|
||||
<span v-if="update.commentsEnabled" class="text-ghost-500">
|
||||
Comments (coming soon)
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,19 +11,19 @@
|
|||
</UFormField>
|
||||
|
||||
<!-- Privacy Settings -->
|
||||
<div class="border border-stone-700 rounded-lg p-4 bg-stone-800/30">
|
||||
<h3 class="text-sm font-medium text-stone-200 mb-4">Privacy Settings</h3>
|
||||
<div class="border border-ghost-700 rounded-lg p-4 bg-ghost-800/30">
|
||||
<h3 class="text-sm font-medium text-ghost-200 mb-4">Privacy Settings</h3>
|
||||
<div class="space-y-3">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
v-model="formData.privacy"
|
||||
type="radio"
|
||||
value="public"
|
||||
class="w-4 h-4 text-stone-400"
|
||||
class="w-4 h-4 text-ghost-400"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-stone-200 font-medium">Public</div>
|
||||
<div class="text-sm text-stone-400">
|
||||
<div class="text-ghost-200 font-medium">Public</div>
|
||||
<div class="text-sm text-ghost-400">
|
||||
Visible to everyone, including non-members
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -34,11 +34,11 @@
|
|||
v-model="formData.privacy"
|
||||
type="radio"
|
||||
value="members"
|
||||
class="w-4 h-4 text-stone-400"
|
||||
class="w-4 h-4 text-ghost-400"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-stone-200 font-medium">Members Only</div>
|
||||
<div class="text-sm text-stone-400">
|
||||
<div class="text-ghost-200 font-medium">Members Only</div>
|
||||
<div class="text-sm text-ghost-400">
|
||||
Only visible to Ghost Guild members
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -49,11 +49,11 @@
|
|||
v-model="formData.privacy"
|
||||
type="radio"
|
||||
value="private"
|
||||
class="w-4 h-4 text-stone-400"
|
||||
class="w-4 h-4 text-ghost-400"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-stone-200 font-medium">Private</div>
|
||||
<div class="text-sm text-stone-400">Only visible to you</div>
|
||||
<div class="text-ghost-200 font-medium">Private</div>
|
||||
<div class="text-sm text-ghost-400">Only visible to you</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -66,15 +66,17 @@
|
|||
<div class="flex items-center gap-3">
|
||||
<USwitch v-model="formData.commentsEnabled" />
|
||||
<div>
|
||||
<div class="text-stone-200 font-medium">Enable Comments</div>
|
||||
<div class="text-sm text-stone-400">
|
||||
<div class="text-ghost-200 font-medium">Enable Comments</div>
|
||||
<div class="text-sm text-ghost-400">
|
||||
Allow members to comment on this update
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex justify-between items-center pt-4 border-t border-stone-700">
|
||||
<div
|
||||
class="flex justify-between items-center pt-4 border-t border-ghost-700"
|
||||
>
|
||||
<UButton variant="ghost" color="neutral" @click="$emit('cancel')">
|
||||
Cancel
|
||||
</UButton>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue