[FEAT] Save name fix
This commit is contained in:
@@ -16,14 +16,19 @@
|
|||||||
<div class="hidden md:flex items-center gap-5">
|
<div class="hidden md:flex items-center gap-5">
|
||||||
<!-- Auth & Projects -->
|
<!-- Auth & Projects -->
|
||||||
<template v-if="authStore.user">
|
<template v-if="authStore.user">
|
||||||
<NavbarProjectActions @open-save-modal="openSaveModal" @open-new-project-modal="isNewProjectModalOpen = true" @open-project-list="isProjectListOpen = true" />
|
<NavbarProjectActions
|
||||||
|
@save-project="handleQuickSave"
|
||||||
|
@open-save-modal="openSaveModal"
|
||||||
|
@open-new-project-modal="isNewProjectModalOpen = true"
|
||||||
|
@open-project-list="isProjectListOpen = true"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="h-4 w-px bg-gray-200 dark:bg-gray-700"></div>
|
<div class="h-4 w-px bg-gray-200 dark:bg-gray-700"></div>
|
||||||
|
|
||||||
<!-- User Dropdown -->
|
<!-- User Dropdown -->
|
||||||
<NavbarUserMenu @open-auth-modal="isAuthModalOpen = true" />
|
<NavbarUserMenu @open-auth-modal="isAuthModalOpen = true" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<button @click="isAuthModalOpen = true" class="btn btn-primary btn-sm shadow-indigo-500/20 shadow-lg">Login / Register</button>
|
<button @click="isAuthModalOpen = true" class="btn btn-primary btn-sm shadow-indigo-500/20 shadow-lg">Login / Register</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,6 +53,7 @@
|
|||||||
:is-open="isMobileMenuOpen"
|
:is-open="isMobileMenuOpen"
|
||||||
@close="isMobileMenuOpen = false"
|
@close="isMobileMenuOpen = false"
|
||||||
@open-help="$emit('open-help')"
|
@open-help="$emit('open-help')"
|
||||||
|
@save-project="handleQuickSave"
|
||||||
@open-save-modal="openSaveModal"
|
@open-save-modal="openSaveModal"
|
||||||
@open-new-project-modal="isNewProjectModalOpen = true"
|
@open-new-project-modal="isNewProjectModalOpen = true"
|
||||||
@open-project-list="isProjectListOpen = true"
|
@open-project-list="isProjectListOpen = true"
|
||||||
@@ -55,12 +61,12 @@
|
|||||||
</nav>
|
</nav>
|
||||||
<AuthModal :is-open="isAuthModalOpen" @close="isAuthModalOpen = false" />
|
<AuthModal :is-open="isAuthModalOpen" @close="isAuthModalOpen = false" />
|
||||||
<ProjectList :is-open="isProjectListOpen" @close="isProjectListOpen = false" @open-project="handleOpenProject" />
|
<ProjectList :is-open="isProjectListOpen" @close="isProjectListOpen = false" @open-project="handleOpenProject" />
|
||||||
<SaveProjectModal :is-open="isSaveProjectModalOpen" :initial-name="projectStore.currentProject?.name" @close="isSaveProjectModalOpen = false" @save="handleSaveProject" />
|
<SaveProjectModal :is-open="isSaveProjectModalOpen" :initial-name="saveModalInitialName" @close="isSaveProjectModalOpen = false" @save="handleSaveProject" />
|
||||||
<NewProjectModal :is-open="isNewProjectModalOpen" @close="isNewProjectModalOpen = false" @create="handleCreateNewProject" />
|
<NewProjectModal :is-open="isNewProjectModalOpen" @close="isNewProjectModalOpen = false" @create="handleCreateNewProject" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import DarkModeToggle from '../utilities/DarkModeToggle.vue';
|
import DarkModeToggle from '../utilities/DarkModeToggle.vue';
|
||||||
import { useAuthStore } from '@/stores/useAuthStore';
|
import { useAuthStore } from '@/stores/useAuthStore';
|
||||||
import AuthModal from '@/components/auth/AuthModal.vue';
|
import AuthModal from '@/components/auth/AuthModal.vue';
|
||||||
@@ -85,27 +91,49 @@
|
|||||||
const isProjectListOpen = ref(false);
|
const isProjectListOpen = ref(false);
|
||||||
const isSaveProjectModalOpen = ref(false);
|
const isSaveProjectModalOpen = ref(false);
|
||||||
const isNewProjectModalOpen = ref(false);
|
const isNewProjectModalOpen = ref(false);
|
||||||
|
const saveMode = ref<'save' | 'save-as'>('save');
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const projectStore = useProjectStore();
|
const projectStore = useProjectStore();
|
||||||
const { createProject, openProject, saveProject } = useProjectManager();
|
const { createProject, openProject, saveProject, saveAsProject } = useProjectManager();
|
||||||
|
|
||||||
const handleOpenProject = async (project: Project) => {
|
const handleOpenProject = async (project: Project) => {
|
||||||
await openProject(project);
|
await openProject(project);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openSaveModal = () => {
|
const openSaveModal = (mode: 'save' | 'save-as' = 'save') => {
|
||||||
|
saveMode.value = mode;
|
||||||
isSaveProjectModalOpen.value = true;
|
isSaveProjectModalOpen.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const saveModalInitialName = computed(() => {
|
||||||
|
if (saveMode.value === 'save-as') {
|
||||||
|
return (projectStore.currentProject?.name ? projectStore.currentProject.name + ' (copy)' : '');
|
||||||
|
}
|
||||||
|
return projectStore.currentProject?.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleQuickSave = async () => {
|
||||||
|
if (projectStore.currentProject?.name) {
|
||||||
|
await saveProject(projectStore.currentProject.name);
|
||||||
|
} else {
|
||||||
|
openSaveModal('save');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSaveProject = async (name: string) => {
|
const handleSaveProject = async (name: string) => {
|
||||||
try {
|
try {
|
||||||
await saveProject(name);
|
if (saveMode.value === 'save-as') {
|
||||||
|
await saveAsProject(name);
|
||||||
|
} else {
|
||||||
|
await saveProject(name);
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Error handled in composable
|
// Error handled in composable
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleCreateNewProject = (config: { width: number; height: number; columns: number; rows: number }) => {
|
const handleCreateNewProject = (config: { width: number; height: number; columns: number; rows: number }) => {
|
||||||
createProject(config);
|
createProject(config);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,12 +19,16 @@
|
|||||||
<div class="border-t border-gray-200 dark:border-gray-800 my-2 pt-2 px-3">
|
<div class="border-t border-gray-200 dark:border-gray-800 my-2 pt-2 px-3">
|
||||||
<p class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2">Project</p>
|
<p class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2">Project</p>
|
||||||
|
|
||||||
<button v-if="isEditorActive" @click="$emit('open-save-modal'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
<button v-if="isEditorActive" @click="$emit('save-project'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
||||||
<i class="fas fa-save w-5"></i> Save project
|
<i class="fas fa-save w-5"></i> Save project
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button v-if="isEditorActive" @click="$emit('open-save-modal', 'save-as'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
||||||
|
<i class="fas fa-clone w-5"></i> Save as...
|
||||||
|
</button>
|
||||||
|
|
||||||
<button @click="$emit('open-new-project-modal'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
<button @click="$emit('open-new-project-modal'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
||||||
<i class="fas fa-plus w-5"></i> New poject
|
<i class="fas fa-plus w-5"></i> New project
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="$emit('open-project-list'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
<button @click="$emit('open-project-list'); $emit('close')" class="w-full text-left flex items-center gap-3 px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
|
||||||
@@ -64,5 +68,5 @@
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits(['close', 'open-help', 'open-save-modal', 'open-new-project-modal', 'open-project-list']);
|
defineEmits(['close', 'open-help', 'save-project', 'open-save-modal', 'open-new-project-modal', 'open-project-list']);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -20,10 +20,16 @@
|
|||||||
|
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<Tooltip v-if="isEditorActive" text="Save project">
|
<Tooltip v-if="isEditorActive" text="Save project">
|
||||||
<button @click="$emit('open-save-modal')" class="w-8 h-8 rounded-lg flex items-center justify-center text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 transition-all">
|
<button @click="$emit('save-project')" class="w-8 h-8 rounded-lg flex items-center justify-center text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 transition-all">
|
||||||
<i class="fas fa-save"></i>
|
<i class="fas fa-save"></i>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip v-if="isEditorActive && projectStore.currentProject" text="Save as...">
|
||||||
|
<button @click="$emit('open-save-modal', 'save-as')" class="w-8 h-8 rounded-lg flex items-center justify-center text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 transition-all">
|
||||||
|
<i class="fas fa-clone"></i>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip text="New project">
|
<Tooltip text="New project">
|
||||||
<button @click="$emit('open-new-project-modal')" class="w-8 h-8 rounded-lg flex items-center justify-center text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 transition-all">
|
<button @click="$emit('open-new-project-modal')" class="w-8 h-8 rounded-lg flex items-center justify-center text-gray-500 hover:text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 transition-all">
|
||||||
@@ -45,7 +51,7 @@
|
|||||||
import { useProjectManager } from '@/composables/useProjectManager';
|
import { useProjectManager } from '@/composables/useProjectManager';
|
||||||
import Tooltip from '@/components/utilities/Tooltip.vue';
|
import Tooltip from '@/components/utilities/Tooltip.vue';
|
||||||
|
|
||||||
defineEmits(['open-save-modal', 'open-project-list', 'open-new-project-modal']);
|
defineEmits(['save-project', 'open-save-modal', 'open-project-list', 'open-new-project-modal']);
|
||||||
|
|
||||||
const projectStore = useProjectStore();
|
const projectStore = useProjectStore();
|
||||||
const { isEditorActive } = useProjectManager();
|
const { isEditorActive } = useProjectManager();
|
||||||
|
|||||||
@@ -1,47 +1,96 @@
|
|||||||
<template>
|
<template>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div v-if="isOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm" @click.self="close">
|
<div v-if="isOpen" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4" @click.self="close">
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-2xl p-6 relative border border-gray-200 dark:border-gray-700 max-h-[80vh] flex flex-col">
|
<div class="bg-white dark:bg-gray-900 rounded-2xl shadow-2xl w-full max-w-3xl overflow-hidden border border-gray-200 dark:border-gray-800 flex flex-col max-h-[85vh] animate-fade-in-up">
|
||||||
<div class="flex items-center justify-between mb-6 shrink-0">
|
|
||||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100">My projects</h2>
|
<!-- Header -->
|
||||||
<button @click="close" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
|
<div class="px-6 py-5 border-b border-gray-100 dark:border-gray-800 flex items-center justify-between bg-gray-50/50 dark:bg-gray-800/20">
|
||||||
|
<div>
|
||||||
|
<h2 class="text-xl font-bold text-gray-900 dark:text-gray-100">My Projects</h2>
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400">Manage your saved sprite sheets</p>
|
||||||
|
</div>
|
||||||
|
<button @click="close" class="w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 overflow-y-auto custom-scrollbar p-1">
|
<!-- Content -->
|
||||||
<div v-if="loading" class="flex justify-center py-8">
|
<div class="flex-1 overflow-y-auto custom-scrollbar p-6 bg-gray-50 dark:bg-black/20">
|
||||||
<i class="fas fa-spinner fa-spin text-2xl text-indigo-500"></i>
|
|
||||||
|
<!-- Loading State -->
|
||||||
|
<div v-if="loading" class="flex flex-col items-center justify-center py-12">
|
||||||
|
<div class="w-10 h-10 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4"></div>
|
||||||
|
<p class="text-gray-500 font-medium">Loading projects...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="projects.length === 0" class="text-center py-12 text-gray-500 dark:text-gray-400">
|
<!-- Empty State -->
|
||||||
<i class="fas fa-folder-open text-4xl mb-3 opacity-50"></i>
|
<div v-else-if="projects.length === 0" class="flex flex-col items-center justify-center py-12 text-center">
|
||||||
<p>No projects found. Save your work to see it here!</p>
|
<div class="w-20 h-20 bg-gray-100 dark:bg-gray-800 rounded-full flex items-center justify-center mb-4">
|
||||||
|
<i class="fas fa-folder-open text-3xl text-gray-400 dark:text-gray-500"></i>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 dark:text-gray-100 mb-2">No projects yet</h3>
|
||||||
|
<p class="text-gray-500 dark:text-gray-400 max-w-xs mb-6">Create your first project or load an example to get started with Sprite Sheet Generator.</p>
|
||||||
|
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button @click="loadExample" class="btn btn-secondary flex items-center gap-2">
|
||||||
|
<i class="fas fa-lightbulb"></i> Load Example
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<!-- Project Grid -->
|
||||||
<div v-for="project in projects" :key="project.id" class="group p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 hover:border-indigo-500 dark:hover:border-indigo-500 transition-all cursor-pointer relative" @click="selectProject(project)">
|
<div v-else class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
<div class="flex items-start justify-between mb-2">
|
<div
|
||||||
<div class="flex-1 min-w-0">
|
v-for="project in projects"
|
||||||
<h3 class="font-bold text-gray-900 dark:text-gray-100 truncate pr-2">{{ project.name }}</h3>
|
:key="project.id"
|
||||||
</div>
|
class="group bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 hover:border-indigo-500 dark:hover:border-indigo-500 hover:shadow-lg dark:hover:shadow-indigo-500/10 transition-all cursor-pointer overflow-hidden flex flex-col h-[180px]"
|
||||||
<div class="opacity-0 group-hover:opacity-100 transition-opacity">
|
@click="selectProject(project)"
|
||||||
<button @click.stop="deleteProject(project.id)" class="text-gray-400 hover:text-red-500 p-1" title="Delete Project">
|
>
|
||||||
|
<!-- Card Header -->
|
||||||
|
<div class="p-4 flex-1">
|
||||||
|
<div class="flex items-start justify-between mb-2">
|
||||||
|
<div class="w-10 h-10 rounded-lg bg-indigo-50 dark:bg-indigo-900/30 flex items-center justify-center text-indigo-500 dark:text-indigo-400 font-bold text-lg shrink-0">
|
||||||
|
{{ project.name.charAt(0).toUpperCase() }}
|
||||||
|
</div>
|
||||||
|
<button @click.stop="deleteProject(project.id)" class="opacity-0 group-hover:opacity-100 text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 p-2 rounded-lg transition-all" title="Delete Project">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3 class="font-bold text-gray-900 dark:text-gray-100 truncate mb-1">{{ project.name }}</h3>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400 line-clamp-2">
|
||||||
|
{{ project.data?.layers?.length || 0 }} layers • {{ project.data?.columns }}x{{ project.data?.columns }} grid
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400 flex flex-col gap-1">
|
<!-- Card Footer -->
|
||||||
<span><i class="fas fa-clock mr-1"></i> Updated: {{ formatDate(project.updated) }}</span>
|
<div class="px-4 py-3 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-100 dark:border-gray-700/50 flex items-center justify-between text-xs">
|
||||||
<span><i class="fas fa-layer-group mr-1"></i> Layers: {{ project.data?.layers?.length || 0 }}</span>
|
<span class="text-gray-500 dark:text-gray-400 font-medium group-hover:text-indigo-500 transition-colors">
|
||||||
|
{{ formatDate(project.updated) }}
|
||||||
|
</span>
|
||||||
|
<span class="text-indigo-600 dark:text-indigo-400 font-bold opacity-0 group-hover:opacity-100 transform translate-x-2 group-hover:translate-x-0 transition-all">
|
||||||
|
Open <i class="fas fa-arrow-right ml-1"></i>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Load Example Card (Visible even if lists are present, as a small helper at the end?) -->
|
||||||
|
<!-- OR keep it separate. Let's keep it simple for now and rely on empty state for the big button,
|
||||||
|
but maybe add a small text link at the bottom or top? -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700 flex justify-end shrink-0">
|
<!-- Footer -->
|
||||||
<button @click="close" class="btn btn-secondary text-sm">Close</button>
|
<div class="px-6 py-4 border-t border-gray-100 dark:border-gray-800 flex justify-between items-center bg-white dark:bg-gray-900">
|
||||||
|
<p class="text-xs text-gray-400" v-if="projects.length > 0">
|
||||||
|
Showing {{ projects.length }} project{{ projects.length !== 1 ? 's' : '' }}
|
||||||
|
</p>
|
||||||
|
<div class="flex gap-2 ml-auto">
|
||||||
|
<button v-if="projects.length > 0" @click="loadExample" class="text-xs font-medium text-gray-500 hover:text-indigo-600 dark:hover:text-indigo-400 px-3 py-2">
|
||||||
|
Load Example
|
||||||
|
</button>
|
||||||
|
<button @click="close" class="btn btn-secondary text-sm">Close</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,6 +100,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, toRefs, watch } from 'vue';
|
import { onMounted, toRefs, watch } from 'vue';
|
||||||
import { useProjectStore, type Project } from '@/stores/useProjectStore';
|
import { useProjectStore, type Project } from '@/stores/useProjectStore';
|
||||||
|
import { useProjectManager } from '@/composables/useProjectManager';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -63,6 +113,7 @@
|
|||||||
|
|
||||||
const projectStore = useProjectStore();
|
const projectStore = useProjectStore();
|
||||||
const { projects, isLoading: loading } = toRefs(projectStore);
|
const { projects, isLoading: loading } = toRefs(projectStore);
|
||||||
|
const { createProject, loadProjectData } = useProjectManager();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.isOpen,
|
() => props.isOpen,
|
||||||
@@ -92,11 +143,52 @@
|
|||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
return new Date(dateString).toLocaleDateString(undefined, {
|
return new Date(dateString).toLocaleDateString(undefined, {
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
hour: '2-digit',
|
year: 'numeric'
|
||||||
minute: '2-digit',
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadExample = async () => {
|
||||||
|
// 1. Create a clean base project (this sets the grid size)
|
||||||
|
createProject({ width: 32, height: 32, columns: 8, rows: 8 });
|
||||||
|
|
||||||
|
// 2. Load some dummy data if we want more than just an empty grid
|
||||||
|
// For now, let's just make sure it creates a fresh state and closes the modal
|
||||||
|
// Ideally we would inject some sprites here but since we don't have easy access to sprite IDs without looking them up
|
||||||
|
// We will just init an empty 8x8 project called "Example Project" if helpful,
|
||||||
|
// or strictly Unsaved (createProject does that).
|
||||||
|
|
||||||
|
// If we want to populate it:
|
||||||
|
/*
|
||||||
|
const exampleData = {
|
||||||
|
layers: [
|
||||||
|
{ id: 'l1', name: 'Body', visible: true, sprites: [] }, // etc
|
||||||
|
],
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
await loadProjectData(exampleData);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For now, an empty 8x8 grid is a good "Example" starting point compared to nothing.
|
||||||
|
|
||||||
|
close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.animate-fade-in-up {
|
||||||
|
animation: fadeInUp 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -69,15 +69,29 @@ export const useProjectManager = () => {
|
|||||||
try {
|
try {
|
||||||
const data = await generateProjectJSON();
|
const data = await generateProjectJSON();
|
||||||
|
|
||||||
if (projectStore.currentProject && projectStore.currentProject.name === name) {
|
if (projectStore.currentProject) {
|
||||||
await projectStore.updateProject(projectStore.currentProject.id, data);
|
// Update existing project (even if name changed)
|
||||||
|
await projectStore.updateProject(projectStore.currentProject.id, name, data);
|
||||||
} else {
|
} else {
|
||||||
|
// Create new project if none exists
|
||||||
await projectStore.createProject(name, data);
|
await projectStore.createProject(name, data);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert('Failed to save project');
|
alert('Failed to save project');
|
||||||
throw e; // Re-throw to let caller know
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveAsProject = async (name: string) => {
|
||||||
|
try {
|
||||||
|
const data = await generateProjectJSON();
|
||||||
|
// Always create new
|
||||||
|
await projectStore.createProject(name, data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert('Failed to save project as new');
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,6 +104,8 @@ export const useProjectManager = () => {
|
|||||||
createProject,
|
createProject,
|
||||||
openProject,
|
openProject,
|
||||||
saveProject,
|
saveProject,
|
||||||
|
saveAsProject,
|
||||||
closeEditor,
|
closeEditor,
|
||||||
|
loadProjectData,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,14 +62,20 @@ export const useProjectStore = defineStore('project', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateProject(id: string, data: any) {
|
async function updateProject(id: string, name: string, data: any) {
|
||||||
if (!authStore.user) return;
|
if (!authStore.user) return;
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const record = await authStore.pb.collection('projects').update(id, {
|
const record = await authStore.pb.collection('projects').update(id, {
|
||||||
|
name,
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
currentProject.value = { ...currentProject.value!, data: record.data, updated: record.updated };
|
currentProject.value = {
|
||||||
|
...currentProject.value!,
|
||||||
|
name: record.name,
|
||||||
|
data: record.data,
|
||||||
|
updated: record.updated
|
||||||
|
};
|
||||||
await fetchProjects();
|
await fetchProjects();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update project:', error);
|
console.error('Failed to update project:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user