fix(album): removing last album image was not working
This commit is contained in:
14
frontend/.prettierrc
Normal file
14
frontend/.prettierrc
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"singleAttributePerLine": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"htmlWhitespaceSensitivity": "ignore",
|
||||||
|
"vueIndentScriptAndStyle": true,
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
||||||
138
frontend/package-lock.json
generated
138
frontend/package-lock.json
generated
@@ -32,10 +32,13 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/webpack-env": "^1.18.8",
|
"@types/webpack-env": "^1.18.8",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
|
"@vue/eslint-config-prettier": "^10.2.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-tailwindcss": "^3.18.0",
|
||||||
"eslint-plugin-vue": "^9.22.0",
|
"eslint-plugin-vue": "^9.22.0",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"vite": "^6.3.1"
|
"vite": "^6.3.1"
|
||||||
}
|
}
|
||||||
@@ -975,6 +978,18 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pkgr/core": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/pkgr"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.1.4",
|
"version": "5.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
||||||
@@ -1554,6 +1569,20 @@
|
|||||||
"rfdc": "^1.4.1"
|
"rfdc": "^1.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/eslint-config-prettier": {
|
||||||
|
"version": "10.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz",
|
||||||
|
"integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"eslint-config-prettier": "^10.0.1",
|
||||||
|
"eslint-plugin-prettier": "^5.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">= 8.21.0",
|
||||||
|
"prettier": ">= 3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
"version": "3.5.14",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.14.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.14.tgz",
|
||||||
@@ -2460,6 +2489,67 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-config-prettier": {
|
||||||
|
"version": "10.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz",
|
||||||
|
"integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/eslint-config-prettier"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eslint-plugin-prettier": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"prettier-linter-helpers": "^1.0.0",
|
||||||
|
"synckit": "^0.11.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/eslint-plugin-prettier"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/eslint": ">=8.0.0",
|
||||||
|
"eslint": ">=8.0.0",
|
||||||
|
"eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
|
||||||
|
"prettier": ">=3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/eslint": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"eslint-config-prettier": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eslint-plugin-tailwindcss": {
|
||||||
|
"version": "3.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.18.0.tgz",
|
||||||
|
"integrity": "sha512-PQDU4ZMzFH0eb2DrfHPpbgo87Zgg2EXSMOj1NSfzdZm+aJzpuwGerfowMIaVehSREEa0idbf/eoNYAOHSJoDAQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-glob": "^3.2.5",
|
||||||
|
"postcss": "^8.4.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": "^3.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-vue": {
|
"node_modules/eslint-plugin-vue": {
|
||||||
"version": "9.33.0",
|
"version": "9.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz",
|
||||||
@@ -2617,6 +2707,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-diff": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
@@ -4090,6 +4186,33 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier-linter-helpers": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-diff": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/property-expr": {
|
"node_modules/property-expr": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
|
||||||
@@ -4626,6 +4749,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/synckit": {
|
||||||
|
"version": "0.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz",
|
||||||
|
"integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@pkgr/core": "^0.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/synckit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.17",
|
"version": "3.4.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||||
|
|||||||
@@ -33,10 +33,13 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/webpack-env": "^1.18.8",
|
"@types/webpack-env": "^1.18.8",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
|
"@vue/eslint-config-prettier": "^10.2.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-tailwindcss": "^3.18.0",
|
||||||
"eslint-plugin-vue": "^9.22.0",
|
"eslint-plugin-vue": "^9.22.0",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"vite": "^6.3.1"
|
"vite": "^6.3.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-4 relative"
|
<div class="relative p-4"
|
||||||
@mouseenter="showEditButtons = isLoggedIn && creatorProfileStore.creator?.id === brandingStore.value.id"
|
@mouseenter="showEditButtons = isLoggedIn && creatorProfileStore.creator?.id === brandingStore.value.id"
|
||||||
@mouseleave="showEditButtons = false">
|
@mouseleave="showEditButtons = false">
|
||||||
|
|
||||||
<!-- Edit buttons with absolute positioning -->
|
<!-- Edit buttons with absolute positioning -->
|
||||||
<div v-if="showEditButtons || isEditMode"
|
<div v-if="showEditButtons || isEditMode" class="absolute right-4 top-4 flex gap-2">
|
||||||
class="absolute top-4 right-4 flex gap-2">
|
|
||||||
|
|
||||||
<!-- Edit button with pencil icon -->
|
<!-- Edit button with pencil icon -->
|
||||||
<button
|
<button v-if="!isEditMode" :title="t('edit')"
|
||||||
v-if="!isEditMode"
|
class="flex size-12 items-center justify-center rounded-full bg-hutopyPrimary shadow-lg"
|
||||||
:title="t('edit')"
|
@click="toggleEditMode()">
|
||||||
class="w-12 h-12 bg-hutopyPrimary rounded-full flex items-center justify-center shadow-lg"
|
|
||||||
@click="toggleEditMode()"
|
|
||||||
>
|
|
||||||
<v-icon large>mdi-pencil</v-icon>
|
<v-icon large>mdi-pencil</v-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Save button -->
|
<!-- Save button -->
|
||||||
<button
|
<button v-if="isEditMode" :disabled="!canSave" :title="t('save')"
|
||||||
v-if="isEditMode"
|
class="flex size-12 items-center justify-center rounded-full bg-hutopyPrimary shadow-lg" @click="saveChanges()">
|
||||||
:disabled="!canSave"
|
|
||||||
:title="t('save')"
|
|
||||||
class="w-12 h-12 bg-hutopyPrimary rounded-full flex items-center justify-center shadow-lg"
|
|
||||||
@click="saveChanges()"
|
|
||||||
>
|
|
||||||
<v-icon large>mdi-check</v-icon>
|
<v-icon large>mdi-check</v-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Cancel button -->
|
<!-- Cancel button -->
|
||||||
<button
|
<button v-if="isEditMode" :title="t('cancel')"
|
||||||
v-if="isEditMode"
|
class="flex size-12 items-center justify-center rounded-full bg-red-500 shadow-lg" @click="cancelEdit">
|
||||||
:title="t('cancel')"
|
|
||||||
class="w-12 h-12 bg-red-500 rounded-full flex items-center justify-center shadow-lg"
|
|
||||||
@click="cancelEdit"
|
|
||||||
>
|
|
||||||
<v-icon large>mdi-close</v-icon>
|
<v-icon large>mdi-close</v-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +29,7 @@
|
|||||||
<!-- MainPage -->
|
<!-- MainPage -->
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
|
||||||
<h1 class="flex justify-start text-2xl font-bold text-center mb-4">
|
<h1 class="mb-4 flex justify-start text-center text-2xl font-bold">
|
||||||
{{ t('creator.sections.about.title') }}
|
{{ t('creator.sections.about.title') }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@@ -50,72 +37,47 @@
|
|||||||
<!-- Description Section -->
|
<!-- Description Section -->
|
||||||
<div>
|
<div>
|
||||||
<div v-if="!isEditMode">
|
<div v-if="!isEditMode">
|
||||||
<p v-if="description" class="text-lg text-justify mb-6 whitespace-pre-line">
|
<p v-if="description" class="mb-6 whitespace-pre-line text-justify text-lg">
|
||||||
{{ description }}
|
{{ description }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<v-textarea v-if="isEditMode"
|
<v-textarea v-if="isEditMode" v-model="editableDescription" :counter="2000" :error-messages="descriptionError"
|
||||||
v-model="editableDescription"
|
:label="t('creator.sections.about.description')" :rules="[
|
||||||
:counter="2000"
|
|
||||||
:error-messages="descriptionError"
|
|
||||||
:label="t('creator.sections.about.description')"
|
|
||||||
:rules="[
|
|
||||||
v => !!v || t('creator.validation.descriptionRequired'),
|
v => !!v || t('creator.validation.descriptionRequired'),
|
||||||
v => v.length <= 2000 || t('creator.validation.descriptionTooLong')
|
v => v.length <= 2000 || t('creator.validation.descriptionTooLong')
|
||||||
]"
|
]" auto-grow class="w-full p-2 py-6" rows="5" variant="outlined"></v-textarea>
|
||||||
auto-grow
|
|
||||||
class="w-full p-2 py-6"
|
|
||||||
rows="5"
|
|
||||||
variant="outlined"></v-textarea>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Video Section -->
|
<!-- Video Section -->
|
||||||
<div v-if="videoUrl || isEditMode"
|
<div v-if="videoUrl || isEditMode" :class="['content-section', {
|
||||||
:class="['content-section', {
|
|
||||||
'rounded-t-xl': hasImages && !isEditMode,
|
'rounded-t-xl': hasImages && !isEditMode,
|
||||||
'rounded-xl': !hasImages && !isEditMode
|
'rounded-xl': !hasImages && !isEditMode
|
||||||
}]">
|
}]">
|
||||||
<div v-if="!isEditMode && videoUrl" class="video-container">
|
<div v-if="!isEditMode && videoUrl" class="video-container">
|
||||||
<iframe
|
<iframe :src="youtubeEmbedUrl"
|
||||||
:src="youtubeEmbedUrl"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen
|
allowfullscreen class="video-frame" title="YouTube video player">
|
||||||
class="video-frame"
|
|
||||||
title="YouTube video player">
|
|
||||||
</iframe>
|
</iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isEditMode">
|
<div v-if="isEditMode">
|
||||||
<v-text-field
|
<v-text-field v-model="editableVideoUrl" :error-messages="videoUrlError"
|
||||||
v-model="editableVideoUrl"
|
:label="t('creator.fields.videoUrl')" class="w-full p-2" type="text" variant="outlined" />
|
||||||
:error-messages="videoUrlError"
|
|
||||||
:label="t('creator.fields.videoUrl')"
|
|
||||||
class="w-full p-2"
|
|
||||||
type="text"
|
|
||||||
variant="outlined"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Photos Section using Album component -->
|
<!-- Photos Section using Album component -->
|
||||||
<div>
|
<div>
|
||||||
<!-- Use AlbumView for display mode -->
|
<!-- Use AlbumView for display mode -->
|
||||||
<AlbumView v-if="!isEditMode && hasImages"
|
<AlbumView v-if="!isEditMode && hasImages" :class="['content-section', {
|
||||||
:class="['content-section', {
|
|
||||||
'rounded-b-xl': videoUrl && !isEditMode,
|
'rounded-b-xl': videoUrl && !isEditMode,
|
||||||
'rounded-xl': !videoUrl && !isEditMode
|
'rounded-xl': !videoUrl && !isEditMode
|
||||||
}]"
|
}]" :images="thumbnailUrls" @photo-click="handlePhotoClick" />
|
||||||
:images="thumbnailUrls"
|
|
||||||
@photo-click="handlePhotoClick"/>
|
|
||||||
|
|
||||||
<AlbumViewer v-model="showAlbumViewer"
|
<AlbumViewer v-model="showAlbumViewer" :images="originalUrls" :start-index="selectedPhotoIndex" />
|
||||||
:images="originalUrls"
|
|
||||||
:start-index="selectedPhotoIndex"/>
|
|
||||||
|
|
||||||
<!-- Use AlbumEditor for edit mode -->
|
<!-- Use AlbumEditor for edit mode -->
|
||||||
<AlbumEditor v-if="isEditMode"
|
<AlbumEditor v-if="isEditMode" :images="photos" @update:images="updateImages" />
|
||||||
:images="thumbnailUrls"
|
|
||||||
@update:images="updateImages"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Contact Information Section -->
|
<!-- Contact Information Section -->
|
||||||
@@ -136,18 +98,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, ref, computed, watch} from "vue";
|
import { onMounted, ref, computed, watch } from "vue";
|
||||||
import {useClient} from "@/plugins/api.js";
|
import { useClient } from "@/plugins/api.js";
|
||||||
import {useBrandingStore} from "@/stores/brandingStore.js";
|
import { useBrandingStore } from "@/stores/brandingStore.js";
|
||||||
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
|
import { useCreatorProfileStore } from "@/stores/creatorProfileStore.js";
|
||||||
import {useI18n} from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import {buildEmbedUrl, isValidYouTubeUrlOrId, extractVideoId} from '@/utils/youtube';
|
import { buildEmbedUrl, isValidYouTubeUrlOrId, extractVideoId } from '@/utils/youtube';
|
||||||
import AlbumEditor from "@/views/creators/AlbumEditor.vue";
|
import AlbumEditor from "@/views/creators/AlbumEditor.vue";
|
||||||
import AlbumView from "@/views/creators/AlbumView.vue";
|
import AlbumView from "@/views/creators/AlbumView.vue";
|
||||||
// Add these imports at the top with your other imports
|
|
||||||
import AlbumViewer from './AlbumViewer.vue';
|
import AlbumViewer from './AlbumViewer.vue';
|
||||||
|
|
||||||
const {t} = useI18n();
|
const { t } = useI18n();
|
||||||
const creatorProfileStore = useCreatorProfileStore();
|
const creatorProfileStore = useCreatorProfileStore();
|
||||||
const brandingStore = useBrandingStore();
|
const brandingStore = useBrandingStore();
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
@@ -162,7 +123,7 @@ const description = ref("");
|
|||||||
const videoUrl = ref("");
|
const videoUrl = ref("");
|
||||||
const phoneNumber = ref("");
|
const phoneNumber = ref("");
|
||||||
const email = ref("");
|
const email = ref("");
|
||||||
const thumbnailUrls = ref([]);
|
const photos = ref([]); //before was thumbnailUrls
|
||||||
const albumId = ref(null);
|
const albumId = ref(null);
|
||||||
const originalPhotos = ref([]);
|
const originalPhotos = ref([]);
|
||||||
// Add these refs with your other refs
|
// Add these refs with your other refs
|
||||||
@@ -195,10 +156,19 @@ const canSave = computed(() => {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const thumbnailUrls = computed(() => {
|
||||||
|
return photos.value.map(photo => photo.thumbnailUrl)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add this computed property to get the original image URLs
|
||||||
|
const originalUrls = computed(() => {
|
||||||
|
return photos.value.map(photo => photo.originalUrl);
|
||||||
|
});
|
||||||
|
|
||||||
// Computed property to check if there are images
|
// Computed property to check if there are images
|
||||||
const hasImages = computed(() => {
|
const hasImages = computed(() => {
|
||||||
// Only consider it has images if there are actual image URLs (not empty strings)
|
// Only consider it has images if there are actual image URLs (not empty strings)
|
||||||
return thumbnailUrls.value.length > 0 && thumbnailUrls.value.some(img => img && img.trim() !== "");
|
return photos.value.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Computed property for YouTube embed URL
|
// Computed property for YouTube embed URL
|
||||||
@@ -253,17 +223,17 @@ async function fetchAlbumData() {
|
|||||||
// Store original photos for comparison
|
// Store original photos for comparison
|
||||||
originalPhotos.value = response.data.photos;
|
originalPhotos.value = response.data.photos;
|
||||||
// Extract photo URLs from the album photos
|
// Extract photo URLs from the album photos
|
||||||
thumbnailUrls.value = response.data.photos.map(photo => photo.thumbnailUrl);
|
photos.value = response.data.photos;
|
||||||
} else {
|
} else {
|
||||||
// Initialize with empty array instead of empty slots
|
// Initialize with empty array instead of empty slots
|
||||||
thumbnailUrls.value = [];
|
photos.value = [];
|
||||||
originalPhotos.value = [];
|
originalPhotos.value = [];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Album might not exist yet, which is fine
|
// Album might not exist yet, which is fine
|
||||||
console.log("Album might not exist yet:", error);
|
console.log("Album might not exist yet:", error);
|
||||||
// Initialize with empty array instead of empty slots
|
// Initialize with empty array instead of empty slots
|
||||||
thumbnailUrls.value = [];
|
photos.value = [];
|
||||||
originalPhotos.value = [];
|
originalPhotos.value = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +253,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// Update images from Album component
|
// Update images from Album component
|
||||||
function updateImages(newImages) {
|
function updateImages(newImages) {
|
||||||
thumbnailUrls.value = newImages;
|
photos.value = newImages;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveChanges() {
|
async function saveChanges() {
|
||||||
@@ -325,8 +295,15 @@ async function saveChanges() {
|
|||||||
description.value = editableDescription.value;
|
description.value = editableDescription.value;
|
||||||
videoUrl.value = extractVideoId(editableVideoUrl.value) || "";
|
videoUrl.value = extractVideoId(editableVideoUrl.value) || "";
|
||||||
|
|
||||||
|
|
||||||
|
// Check for deleted photos
|
||||||
|
const deletedPhotos = originalPhotos.value.filter(originalPhoto => {
|
||||||
|
// If the photo URL is not in the current images array, it was deleted
|
||||||
|
return !photos.value.includes(originalPhoto.thumbnailUrl);
|
||||||
|
});
|
||||||
|
|
||||||
// Save album photos if they've changed
|
// Save album photos if they've changed
|
||||||
if (thumbnailUrls.value.length > 0) {
|
if (photos.value.length > 0 || deletedPhotos.length > 0) {
|
||||||
// Create or update the album
|
// Create or update the album
|
||||||
const albumId = brandingStore.value.id;
|
const albumId = brandingStore.value.id;
|
||||||
|
|
||||||
@@ -342,12 +319,6 @@ async function saveChanges() {
|
|||||||
console.log("Album might already exist:", error);
|
console.log("Album might already exist:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for deleted photos
|
|
||||||
const deletedPhotos = originalPhotos.value.filter(originalPhoto => {
|
|
||||||
// If the photo URL is not in the current images array, it was deleted
|
|
||||||
return !thumbnailUrls.value.includes(originalPhoto.thumbnailUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete removed photos
|
// Delete removed photos
|
||||||
for (const photo of deletedPhotos) {
|
for (const photo of deletedPhotos) {
|
||||||
try {
|
try {
|
||||||
@@ -358,17 +329,30 @@ async function saveChanges() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now add or update photos
|
// Now add or update photos
|
||||||
for (let i = 0; i < thumbnailUrls.value.length; i++) {
|
for (let i = 0; i < photos.value.length; i++) {
|
||||||
const imageUrl = thumbnailUrls.value[i];
|
const imageData = photos.value[i];
|
||||||
if (imageUrl && imageUrl.startsWith('data:')) {
|
console.log('Image Data to be uploaded:', imageData);
|
||||||
|
if (imageData && imageData.image && imageData.image.originalUrl.startsWith('data:')) {
|
||||||
// This is a new image that needs to be uploaded
|
// This is a new image that needs to be uploaded
|
||||||
const photoId = crypto.randomUUID();
|
const photoId = crypto.randomUUID();
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
|
// Extract MIME type from data URL
|
||||||
|
const mimeMatch = imageData.image.originalUrl.match(/^data:(.*?);base64,/);
|
||||||
|
if (!mimeMatch) {
|
||||||
|
console.warn(`Invalid data URL at index ${i}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mimeType = mimeMatch[1];
|
||||||
|
|
||||||
|
// Determine file extension from MIME type
|
||||||
|
const extension = mimeType.split('/')[1]; // e.g., "jpeg", "png", "webp", etc.
|
||||||
|
|
||||||
// Convert data URL to file
|
// Convert data URL to file
|
||||||
const response = await fetch(imageUrl);
|
const response = await fetch(imageData.image.originalUrl);
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const file = new File([blob], `photo-${i}.jpg`, {type: 'image/jpeg'});
|
const file = new File([blob], `photo-${i}.${extension}`, { type: mimeType });
|
||||||
|
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
@@ -404,12 +388,6 @@ function cancelEdit() {
|
|||||||
// Désactiver le mode édition
|
// Désactiver le mode édition
|
||||||
isEditMode.value = false;
|
isEditMode.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add this computed property to get the original image URLs
|
|
||||||
const originalUrls = computed(() => {
|
|
||||||
return originalPhotos.value.map(photo => photo.originalUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add this function to handle photo clicks
|
// Add this function to handle photo clicks
|
||||||
function handlePhotoClick(index) {
|
function handlePhotoClick(index) {
|
||||||
selectedPhotoIndex.value = index;
|
selectedPhotoIndex.value = index;
|
||||||
@@ -425,7 +403,8 @@ function handlePhotoClick(index) {
|
|||||||
.video-container {
|
.video-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: 31.25%; /* Reduced from 56.25% to make it shorter while maintaining aspect ratio */
|
padding-top: 31.25%;
|
||||||
|
/* Reduced from 56.25% to make it shorter while maintaining aspect ratio */
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="album-editor">
|
<div class="album-editor">
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold mb-4">
|
<h2 class="mb-4 text-xl font-semibold">
|
||||||
{{ t('title') }}
|
{{ t('title') }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<!-- Drop zone with photos -->
|
<!-- Drop zone with photos -->
|
||||||
<div class="drop-zone"
|
<div class="drop-zone" @dragover.prevent @drop.prevent="handleDrop" @click="triggerFileInput">
|
||||||
@dragover.prevent
|
|
||||||
@drop.prevent="handleDrop"
|
|
||||||
@click="triggerFileInput">
|
|
||||||
|
|
||||||
<!-- Upload prompt -->
|
<!-- Upload prompt -->
|
||||||
<div class="drop-zone-content">
|
<div class="drop-zone-content">
|
||||||
<v-icon size="large">mdi-plus</v-icon>
|
<v-icon size="large">mdi-plus</v-icon>
|
||||||
<span class="text-sm mt-2">{{ t('dropzoneText') }}</span>
|
<span class="mt-2 text-sm">{{ t('dropzoneText') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hidden file input -->
|
<!-- Hidden file input -->
|
||||||
<input
|
<input type="file" ref="fileInput" @change="handleFileUpload" accept="image/*" multiple class="hidden" />
|
||||||
type="file"
|
|
||||||
ref="fileInput"
|
|
||||||
@change="handleFileUpload"
|
|
||||||
accept="image/*"
|
|
||||||
multiple
|
|
||||||
class="hidden"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Photos grid -->
|
<!-- Photos grid -->
|
||||||
<draggable
|
<draggable v-model="localImages" class="photos-grid" item-key="id" @end="handleReorder">
|
||||||
v-model="localImages"
|
|
||||||
class="photos-grid"
|
|
||||||
item-key="id"
|
|
||||||
@end="handleReorder"
|
|
||||||
>
|
|
||||||
<template #item="{ element, index }">
|
<template #item="{ element, index }">
|
||||||
<div class="photo-wrapper" @click.stop="toggleMobileControls(index)">
|
<div class="photo-wrapper" @click.stop="toggleMobileControls(index)">
|
||||||
<div class="index-bubble">{{ index + 1 }}</div>
|
<div class="index-bubble">{{ index + 1 }}</div>
|
||||||
<img :src="element.url" :alt="'Image ' + (index + 1)" />
|
<img :src="element.image.originalUrl" :alt="'Image ' + (index + 1)" />
|
||||||
<!-- Processing spinner overlay -->
|
<!-- Processing spinner overlay -->
|
||||||
<div v-if="element.isProcessing" class="loading-overlay">
|
<div v-if="element.isProcessing" class="loading-overlay">
|
||||||
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
||||||
<span class="text-white text-sm mt-2">{{ t('processing') }}</span>
|
<span class="mt-2 text-sm text-white">{{ t('processing') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Upload spinner overlay -->
|
<!-- Upload spinner overlay -->
|
||||||
<div v-if="element.isUploading" class="loading-overlay uploading">
|
<div v-if="element.isUploading" class="loading-overlay uploading">
|
||||||
<v-progress-circular indeterminate color="secondary"></v-progress-circular>
|
<v-progress-circular indeterminate color="secondary"></v-progress-circular>
|
||||||
<span class="text-white text-sm mt-2">{{ t('uploading') }}</span>
|
<span class="mt-2 text-sm text-white">{{ t('uploading') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Left arrow -->
|
<!-- Left arrow -->
|
||||||
<button @click.stop="moveImage(index, 'up')"
|
<button @click.stop="moveImage(index, 'up')" class="action-btn left-btn" :disabled="index === 0"
|
||||||
class="action-btn left-btn"
|
:title="t('moveLeft')" :class="{ 'mobile-active': activePhotoIndex === index }">
|
||||||
:disabled="index === 0"
|
|
||||||
:title="t('moveLeft')"
|
|
||||||
:class="{'mobile-active': activePhotoIndex === index}">
|
|
||||||
<v-icon>mdi-arrow-left</v-icon>
|
<v-icon>mdi-arrow-left</v-icon>
|
||||||
</button>
|
</button>
|
||||||
<!-- Right arrow -->
|
<!-- Right arrow -->
|
||||||
<button @click.stop="moveImage(index, 'down')"
|
<button @click.stop="moveImage(index, 'down')" class="action-btn right-btn"
|
||||||
class="action-btn right-btn"
|
:disabled="index === localImages.length - 1" :title="t('moveRight')"
|
||||||
:disabled="index === localImages.length - 1"
|
:class="{ 'mobile-active': activePhotoIndex === index }">
|
||||||
:title="t('moveRight')"
|
|
||||||
:class="{'mobile-active': activePhotoIndex === index}">
|
|
||||||
<v-icon>mdi-arrow-right</v-icon>
|
<v-icon>mdi-arrow-right</v-icon>
|
||||||
</button>
|
</button>
|
||||||
<!-- Delete button -->
|
<!-- Delete button -->
|
||||||
<button @click.stop="deleteImage(index)"
|
<button @click.stop="deleteImage(index)" class="action-btn delete-btn" :title="t('delete')"
|
||||||
class="action-btn delete-btn"
|
:class="{ 'mobile-active': activePhotoIndex === index }">
|
||||||
:title="t('delete')"
|
|
||||||
:class="{'mobile-active': activePhotoIndex === index}">
|
|
||||||
<v-icon>mdi-delete</v-icon>
|
<v-icon>mdi-delete</v-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,8 +58,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted, watch } from "vue";
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { v7 } from 'uuid';
|
||||||
import draggable from 'vuedraggable';
|
import draggable from 'vuedraggable';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -100,24 +79,88 @@ const activePhotoIndex = ref(null); // Track which photo is currently active for
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Initialize local images with IDs and states
|
// Initialize local images with IDs and states
|
||||||
localImages.value = props.images.map((url, index) => ({
|
localImages.value = props.images.map((image) => ({
|
||||||
id: index,
|
image: image,
|
||||||
url: url,
|
file: null,
|
||||||
isProcessing: false,
|
isProcessing: false,
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
file: null // Store the actual file for upload
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Add event listener to close active controls when clicking outside
|
// Add event listener to close active controls when clicking outside
|
||||||
document.addEventListener('click', closeActiveControls);
|
document.addEventListener('click', closeActiveControls);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close active controls when component is unmounted
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('click', closeActiveControls);
|
document.removeEventListener('click', closeActiveControls);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to handle mobile control visibility
|
function closeActiveControls() {
|
||||||
|
activePhotoIndex.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(localImages, (newVal) => {
|
||||||
|
console.log('localImages changed:', newVal);
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
function handleFiles(files) {
|
||||||
|
console.log('handleFiles:', files)
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
try {
|
||||||
|
const reader = new FileReader();
|
||||||
|
// Create a temporary image object with processing state
|
||||||
|
const tempImage = {
|
||||||
|
image: {
|
||||||
|
id: v7(),
|
||||||
|
originalUrl: '',
|
||||||
|
},
|
||||||
|
file: file,
|
||||||
|
isProcessing: true,
|
||||||
|
isUploading: false,
|
||||||
|
};
|
||||||
|
localImages.value.push(tempImage);
|
||||||
|
|
||||||
|
console.log('Processing image:', tempImage);
|
||||||
|
|
||||||
|
reader.onload = (e) => {
|
||||||
|
console.log('Image loaded:', e);
|
||||||
|
const index = localImages.value.findIndex(local => local.image.id === tempImage.image.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
localImages.value[index].image.originalUrl = e.target.result;
|
||||||
|
localImages.value[index].isProcessing = false;
|
||||||
|
emit('update:images', localImages.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing image:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleDrop(event) {
|
||||||
|
console.log('Drop triggered');
|
||||||
|
const files = Array.from(event.dataTransfer.files);
|
||||||
|
handleFiles(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerFileInput() {
|
||||||
|
console.log('Input triggered');
|
||||||
|
fileInput.value.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileUpload(event) {
|
||||||
|
console.log('File input triggered');
|
||||||
|
const files = Array.from(event.target.files);
|
||||||
|
handleFiles(files);
|
||||||
|
event.target.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReorder() {
|
||||||
|
activePhotoIndex.value = null; // Reset active photo
|
||||||
|
emit('update:images', localImages.value);
|
||||||
|
}
|
||||||
|
|
||||||
function toggleMobileControls(index) {
|
function toggleMobileControls(index) {
|
||||||
// If clicking the same photo, toggle the controls
|
// If clicking the same photo, toggle the controls
|
||||||
if (activePhotoIndex.value === index) {
|
if (activePhotoIndex.value === index) {
|
||||||
@@ -128,78 +171,6 @@ function toggleMobileControls(index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close active controls when clicking outside
|
|
||||||
function closeActiveControls() {
|
|
||||||
activePhotoIndex.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger file input click
|
|
||||||
function triggerFileInput() {
|
|
||||||
fileInput.value.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add drop handler
|
|
||||||
function handleDrop(event) {
|
|
||||||
const files = Array.from(event.dataTransfer.files);
|
|
||||||
handleFiles(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract file handling logic
|
|
||||||
function handleFiles(files) {
|
|
||||||
for (const file of files) {
|
|
||||||
if (file.type.startsWith('image/')) {
|
|
||||||
try {
|
|
||||||
const reader = new FileReader();
|
|
||||||
// Create a temporary image object with processing state
|
|
||||||
const tempImage = {
|
|
||||||
id: Date.now() + Math.random(),
|
|
||||||
url: '',
|
|
||||||
isProcessing: true,
|
|
||||||
isUploading: false,
|
|
||||||
file: file // Store the file for later upload
|
|
||||||
};
|
|
||||||
localImages.value.push(tempImage);
|
|
||||||
|
|
||||||
reader.onload = (e) => {
|
|
||||||
const index = localImages.value.findIndex(img => img.id === tempImage.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
localImages.value[index] = {
|
|
||||||
...tempImage,
|
|
||||||
url: e.target.result,
|
|
||||||
isProcessing: false
|
|
||||||
};
|
|
||||||
emit('update:images', localImages.value.map(img => img.url));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error processing image:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update file upload handler to use common function
|
|
||||||
function handleFileUpload(event) {
|
|
||||||
const files = Array.from(event.target.files);
|
|
||||||
handleFiles(files);
|
|
||||||
event.target.value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete an image
|
|
||||||
function deleteImage(index) {
|
|
||||||
localImages.value.splice(index, 1);
|
|
||||||
activePhotoIndex.value = null; // Reset active photo
|
|
||||||
emit('update:images', localImages.value.map(img => img.url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle reorder after drag and drop
|
|
||||||
function handleReorder() {
|
|
||||||
activePhotoIndex.value = null; // Reset active photo
|
|
||||||
emit('update:images', localImages.value.map(img => img.url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add back the moveImage function
|
|
||||||
function moveImage(index, direction) {
|
function moveImage(index, direction) {
|
||||||
const newIndex = direction === 'up' ? index - 1 : index + 1;
|
const newIndex = direction === 'up' ? index - 1 : index + 1;
|
||||||
if (newIndex >= 0 && newIndex < localImages.value.length) {
|
if (newIndex >= 0 && newIndex < localImages.value.length) {
|
||||||
@@ -207,10 +178,16 @@ function moveImage(index, direction) {
|
|||||||
localImages.value[index] = localImages.value[newIndex];
|
localImages.value[index] = localImages.value[newIndex];
|
||||||
localImages.value[newIndex] = temp;
|
localImages.value[newIndex] = temp;
|
||||||
activePhotoIndex.value = newIndex; // Keep the moved image active
|
activePhotoIndex.value = newIndex; // Keep the moved image active
|
||||||
emit('update:images', localImages.value.map(img => img.url));
|
emit('update:images', localImages.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteImage(index) {
|
||||||
|
localImages.value.splice(index, 1);
|
||||||
|
activePhotoIndex.value = null; // Reset active photo
|
||||||
|
emit('update:images', localImages.value);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="creator-home">
|
<div class="creator-home">
|
||||||
|
|
||||||
<!-- Content sections container -->
|
<!-- Content sections container -->
|
||||||
@@ -6,17 +6,14 @@
|
|||||||
|
|
||||||
<!-- Donation Section -->
|
<!-- Donation Section -->
|
||||||
<div v-if="brandingStore.value?.acceptDonation" class="section sm:hidden">
|
<div v-if="brandingStore.value?.acceptDonation" class="section sm:hidden">
|
||||||
<DonationButton
|
<DonationButton :creator-id="brandingStore.value?.id" :creator-name="brandingStore.value?.name"
|
||||||
:creator-id="brandingStore.value?.id"
|
|
||||||
:creator-name="brandingStore.value?.name"
|
|
||||||
:on-cancelled-url="baseURL + '/paymentfailed/' + brandingStore.value?.id"
|
:on-cancelled-url="baseURL + '/paymentfailed/' + brandingStore.value?.id"
|
||||||
:on-success-url="baseURL + '/paymentcompleted/' + brandingStore.value?.id"
|
:on-success-url="baseURL + '/paymentcompleted/' + brandingStore.value?.id" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- About Creator Section -->
|
<!-- About Creator Section -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<AboutCreator/>
|
<AboutCreator />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -26,7 +23,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AboutCreator from './AboutCreator.vue';
|
import AboutCreator from './AboutCreator.vue';
|
||||||
import DonationButton from "@/views/creators/DonationButton.vue";
|
import DonationButton from "@/views/creators/DonationButton.vue";
|
||||||
import {useBrandingStore} from "@/stores/brandingStore.js";
|
import { useBrandingStore } from "@/stores/brandingStore.js";
|
||||||
|
|
||||||
const brandingStore = useBrandingStore();
|
const brandingStore = useBrandingStore();
|
||||||
const baseURL = window.location.origin;
|
const baseURL = window.location.origin;
|
||||||
@@ -61,7 +58,6 @@ const baseURL = window.location.origin;
|
|||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
|
|||||||
Reference in New Issue
Block a user