Toggle favorite working
This commit is contained in:
parent
5e3f4ec621
commit
4ea2871c6d
13 changed files with 300 additions and 169 deletions
|
|
@ -1,3 +1,11 @@
|
|||
Alt:
|
||||
|
||||
----
|
||||
|
||||
Favoriteforusers:
|
||||
|
||||
----
|
||||
|
||||
Description:
|
||||
|
||||
----
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
Alt:
|
||||
|
||||
----
|
||||
|
||||
Favoriteforusers: - user://WWjXgPWk
|
||||
|
||||
----
|
||||
|
||||
Favoriteforclients:
|
||||
|
||||
- page://G418qZ4ABsoWFx4i
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ accept:
|
|||
type: image
|
||||
|
||||
fields:
|
||||
alt:
|
||||
label: Texte alternatif
|
||||
type: text
|
||||
favoriteForUsers:
|
||||
label: |
|
||||
Dans les favoris des utilisateurs :
|
||||
type: users
|
||||
disabled: true
|
||||
|
|
|
|||
|
|
@ -16,89 +16,10 @@ return [
|
|||
'language' => 'fr',
|
||||
'css' => 'assets/css/panel.css',
|
||||
'favicon' => 'assets/favicons/favicon.svg',
|
||||
'menu' => [
|
||||
'site' => [
|
||||
'label' => 'Dashboard',
|
||||
'icon' => 'dashboard',
|
||||
'current' => function (string $current): bool {
|
||||
// the links of all your custom menu entries
|
||||
$links = [
|
||||
'pages/clients',
|
||||
'pages/projects',
|
||||
'pages/inspirations',
|
||||
'pages/notifications',
|
||||
'pages/events'
|
||||
];
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return
|
||||
$current === 'site' &&
|
||||
A::every($links, fn ($link) => Str::contains($path, $link) === false);
|
||||
}
|
||||
],
|
||||
'clients' => [
|
||||
'label' => 'Clients',
|
||||
'icon' => 'account',
|
||||
'link' => 'pages/clients',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/clients');
|
||||
}
|
||||
],
|
||||
'projects' => [
|
||||
'label' => 'Projets',
|
||||
'icon' => 'folder',
|
||||
'link' => 'pages/projects',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/projects');
|
||||
}
|
||||
],
|
||||
'inspirations' => [
|
||||
'label' => 'Inspirations',
|
||||
'icon' => 'images',
|
||||
'link' => 'pages/inspirations',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/inspirations');
|
||||
}
|
||||
],
|
||||
'-',
|
||||
'notifications' => [
|
||||
'label' => 'Notifications',
|
||||
'icon' => 'bell',
|
||||
'link' => 'pages/notifications',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/notifications');
|
||||
}
|
||||
],
|
||||
'events' => [
|
||||
'label' => 'Événements',
|
||||
'icon' => 'calendar',
|
||||
'link' => 'pages/events',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/events');
|
||||
}
|
||||
],
|
||||
'-',
|
||||
'users',
|
||||
'system'
|
||||
]
|
||||
'menu' => require(__DIR__ . '/menu.php'),
|
||||
],
|
||||
'routes' => [
|
||||
[
|
||||
'pattern' => '(:all)logout.php',
|
||||
'action' => function () {
|
||||
$kirby = kirby();
|
||||
$user = $kirby->user();
|
||||
$user->logout();
|
||||
session_start();
|
||||
|
||||
go($_SESSION['redirect_url']);
|
||||
|
||||
return '<html><body>logout</body></html>';
|
||||
}
|
||||
]
|
||||
require(__DIR__ . '/routes/logout.php'),
|
||||
require(__DIR__ . '/routes/toggle-favorite.php'),
|
||||
]
|
||||
];
|
||||
|
|
|
|||
71
public/site/config/menu.php
Normal file
71
public/site/config/menu.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'site' => [
|
||||
'label' => 'Dashboard',
|
||||
'icon' => 'dashboard',
|
||||
'current' => function (string $current): bool {
|
||||
// the links of all your custom menu entries
|
||||
$links = [
|
||||
'pages/clients',
|
||||
'pages/projects',
|
||||
'pages/inspirations',
|
||||
'pages/notifications',
|
||||
'pages/events'
|
||||
];
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return
|
||||
$current === 'site' &&
|
||||
A::every($links, fn ($link) => Str::contains($path, $link) === false);
|
||||
}
|
||||
],
|
||||
'clients' => [
|
||||
'label' => 'Clients',
|
||||
'icon' => 'account',
|
||||
'link' => 'pages/clients',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/clients');
|
||||
}
|
||||
],
|
||||
'projects' => [
|
||||
'label' => 'Projets',
|
||||
'icon' => 'folder',
|
||||
'link' => 'pages/projects',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/projects');
|
||||
}
|
||||
],
|
||||
'inspirations' => [
|
||||
'label' => 'Inspirations',
|
||||
'icon' => 'images',
|
||||
'link' => 'pages/inspirations',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/inspirations');
|
||||
}
|
||||
],
|
||||
'-',
|
||||
'notifications' => [
|
||||
'label' => 'Notifications',
|
||||
'icon' => 'bell',
|
||||
'link' => 'pages/notifications',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/notifications');
|
||||
}
|
||||
],
|
||||
'events' => [
|
||||
'label' => 'Événements',
|
||||
'icon' => 'calendar',
|
||||
'link' => 'pages/events',
|
||||
'current' => function (string $current): bool {
|
||||
$path = Kirby\Cms\App::instance()->path();
|
||||
return Str::contains($path, 'pages/events');
|
||||
}
|
||||
],
|
||||
'-',
|
||||
'users',
|
||||
'system'
|
||||
];
|
||||
13
public/site/config/routes/logout.php
Normal file
13
public/site/config/routes/logout.php
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'pattern' => '(:all)logout.php',
|
||||
'action' => function () {
|
||||
$kirby = kirby();
|
||||
$user = $kirby->user();
|
||||
$user->logout();
|
||||
session_start();
|
||||
|
||||
go(site()->url());
|
||||
}
|
||||
];
|
||||
33
public/site/config/routes/toggle-favorite.php
Normal file
33
public/site/config/routes/toggle-favorite.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'pattern' => '(:all)toggle-favorite.json',
|
||||
'method' => 'post',
|
||||
'action' => function () {
|
||||
$json = file_get_contents('php://input');
|
||||
$data = json_decode($json);
|
||||
|
||||
$fileName = $data->fileName;
|
||||
|
||||
$page = page($data->inspirationUri);
|
||||
$file = $page->files()->find($fileName);
|
||||
|
||||
$user = Find::user($data->userUuid);
|
||||
$favoriteForUsers = $file->favoriteForUsers()->toUsers();
|
||||
|
||||
if ($favoriteForUsers->has($user)) {
|
||||
$favoriteForUsers->remove($user);
|
||||
} else {
|
||||
$favoriteForUsers->add($user);
|
||||
}
|
||||
|
||||
$array = $favoriteForUsers->toArray();
|
||||
$yaml = Data::encode($array, 'yaml');
|
||||
|
||||
$file->update([
|
||||
'favoriteForUsers' => $yaml
|
||||
]);
|
||||
|
||||
return $favoriteForUsers->toJson();
|
||||
}
|
||||
];
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
<script>
|
||||
<?php if ($kirby->user()): ?>
|
||||
const kirbyData = {
|
||||
user: {
|
||||
role: '<?= $kirby->user()->role() ?>',
|
||||
uuid: '<?= $kirby->user()->uuid() ?>',
|
||||
<?php if ($kirby->user()->role() == 'client'): ?>
|
||||
client: {
|
||||
name: '<?= $kirby->user()->client()->toPage()->title() ?>',
|
||||
|
|
@ -10,6 +12,7 @@
|
|||
<?php endif ?>
|
||||
}
|
||||
}
|
||||
<?php endif ?>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
<?php snippet('header') ?>
|
||||
<div id="app"></div>
|
||||
<?php
|
||||
if ($kirby->user()) {
|
||||
echo '<div id="app"></div>';
|
||||
} else {
|
||||
go($site->panel()->url());
|
||||
}
|
||||
?>
|
||||
<?php snippet('footer') ?>
|
||||
|
|
@ -7,12 +7,16 @@ $inspirations = $page->children()->map(function ($child) {
|
|||
'new' => $child->new()->value() === "true" ? true : false,
|
||||
'date' => $child->date()->toDate('Y-MM-d'),
|
||||
'url' => $child->url(),
|
||||
'uri' => $child->uri(),
|
||||
'modified' => $child->modified('Y-MM-d'),
|
||||
'status' => $child->status(),
|
||||
'cover' => $child->cover()->toFile()->url(),
|
||||
'media' => $child->media()->toFiles()->map(function ($file) {
|
||||
return [
|
||||
'url' => $file->url()
|
||||
'url' => $file->url(),
|
||||
'ald' => $file->alt()->value(),
|
||||
'favoriteForUsers' => $file->favoriteForUsers()->value(),
|
||||
'name' => $file->filename()
|
||||
];
|
||||
})->values()
|
||||
];
|
||||
|
|
|
|||
114
src/components/inspirations/Image.vue
Normal file
114
src/components/inspirations/Image.vue
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<template>
|
||||
<figure class="flex" :style="'--rows: ' + (index % 2 ? 2 : 3)">
|
||||
<button
|
||||
class="favorite"
|
||||
aria-label="Ajouter aux favoris"
|
||||
:aria-pressed="isFavorite"
|
||||
@click="isFavorite = !isFavorite"
|
||||
>
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.49289 22.5821C5.50453 22.5937 5.51646 22.6051 5.52866 22.6161L19.3287 35.1161C19.7097 35.4613 20.2903 35.4613 20.6713 35.1161L34.4713 22.6161C34.4835 22.6051 34.4955 22.5937 34.5071 22.5821C38.7044 18.3848 37.6545 12.2706 34.2529 8.87931C32.5281 7.15976 30.1519 6.07621 27.4679 6.26656C25.0319 6.43932 22.4673 7.65151 20 10.1579C17.5321 7.65095 14.9651 6.43633 12.5266 6.25964C9.83985 6.06497 7.45863 7.14246 5.72977 8.85914C2.3178 12.247 1.27257 18.3618 5.49289 22.5821Z"
|
||||
fill="currentColor"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<img :src="item.url" :alt="item.alt" :data-id="item.id" />
|
||||
</figure>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { useUserStore } from "../../stores/user";
|
||||
import { useApiStore } from "../../stores/api";
|
||||
|
||||
const { item, inspirationUri } = defineProps({
|
||||
item: Object,
|
||||
inspirationUri: String,
|
||||
});
|
||||
|
||||
const toggleFavoriteRoute = "/toggle-favorite.json";
|
||||
const api = useApiStore();
|
||||
const { user } = useUserStore();
|
||||
const isFavorite = ref(item.favoriteForUsers?.includes(user.uuid) ?? false);
|
||||
|
||||
watch(isFavorite, (value) => {
|
||||
const data = {
|
||||
fileName: item.name,
|
||||
userUuid: user.uuid,
|
||||
inspirationUri,
|
||||
};
|
||||
api
|
||||
.fetchRoute(toggleFavoriteRoute, "POST", data)
|
||||
.then((res) =>
|
||||
console.log("Image dans les favoris des utilisateurs : ", res)
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
figure {
|
||||
position: relative;
|
||||
grid-row: span var(--rows);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border-radius: var(--rounded-2xl);
|
||||
overflow: hidden;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
figure:hover::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--color-black-20);
|
||||
z-index: 1;
|
||||
}
|
||||
figure:hover .favorite {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
/* Favorite button */
|
||||
.favorite {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
margin: var(--space-8);
|
||||
padding: var(--space-8);
|
||||
color: var(--color-grey-400);
|
||||
display: none;
|
||||
z-index: 10;
|
||||
}
|
||||
.favorite[aria-pressed="false"]:hover::before {
|
||||
content: "Ajouter dans mes favoris";
|
||||
background: var(--color-background);
|
||||
padding: 1.3rem 3.5rem 1.25rem 1rem;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
inset: -2px;
|
||||
left: unset;
|
||||
width: max-content;
|
||||
border-radius: var(--rounded-xl);
|
||||
color: var(--color-grey-700);
|
||||
letter-spacing: var(--tracking-wide);
|
||||
font-weight: 500;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.favorite[aria-pressed="true"] {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -97,5 +97,28 @@ export const useApiStore = defineStore("api", () => {
|
|||
});
|
||||
}
|
||||
|
||||
return { fetchDataThroughKQL, fetchPageData };
|
||||
async function fetchRoute(path, method, data) {
|
||||
const config = {
|
||||
method: method,
|
||||
};
|
||||
|
||||
if (data) {
|
||||
config.body = JSON.stringify(data);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(path, config);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("La route " + path + " a fonctionné.");
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("La route " + path + " n'a pas fonctionné.", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return { fetchDataThroughKQL, fetchPageData, fetchRoute };
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,34 +9,12 @@
|
|||
<section :id="currentTab" class="inspiration">
|
||||
<Header :inspiration="currentInspiration" />
|
||||
<div class="grid masonry">
|
||||
<template v-for="(item, index) in currentInspiration.media">
|
||||
<figure class="flex" :style="'--rows: ' + (index % 2 ? 2 : 3)">
|
||||
<button
|
||||
class="favorite"
|
||||
aria-label="Ajouter aux favoris"
|
||||
aria-pressed="false"
|
||||
onclick="this.setAttribute('aria-pressed', this.getAttribute('aria-pressed') === 'true' ? 'false' : 'true');"
|
||||
>
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.49289 22.5821C5.50453 22.5937 5.51646 22.6051 5.52866 22.6161L19.3287 35.1161C19.7097 35.4613 20.2903 35.4613 20.6713 35.1161L34.4713 22.6161C34.4835 22.6051 34.4955 22.5937 34.5071 22.5821C38.7044 18.3848 37.6545 12.2706 34.2529 8.87931C32.5281 7.15976 30.1519 6.07621 27.4679 6.26656C25.0319 6.43932 22.4673 7.65151 20 10.1579C17.5321 7.65095 14.9651 6.43633 12.5266 6.25964C9.83985 6.06497 7.45863 7.14246 5.72977 8.85914C2.3178 12.247 1.27257 18.3618 5.49289 22.5821Z"
|
||||
fill="currentColor"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<img :src="item.url" alt="" />
|
||||
</figure>
|
||||
</template>
|
||||
<Image
|
||||
v-for="(item, index) in currentInspiration.media"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:inspirationUri="currentInspiration.uri"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
|
@ -48,6 +26,7 @@ import Menu from "../components/Menu.vue";
|
|||
import Selector from "../components/inspirations/Selector.vue";
|
||||
import Header from "../components/inspirations/Header.vue";
|
||||
import Tabs from "../components/Tabs.vue";
|
||||
import Image from "../components/inspirations/Image.vue";
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
const { data } = defineProps({
|
||||
|
|
@ -94,60 +73,4 @@ function changeTab(newValue) {
|
|||
grid-auto-rows: 12rem;
|
||||
grid-auto-flow: dense;
|
||||
}
|
||||
.masonry > * {
|
||||
position: relative;
|
||||
grid-row: span var(--rows);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border-radius: var(--rounded-2xl);
|
||||
overflow: hidden;
|
||||
}
|
||||
.masonry img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.masonry > *:hover::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--color-black-20);
|
||||
z-index: 1;
|
||||
}
|
||||
.masonry > *:hover .favorite {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
/* Favorite button */
|
||||
.favorite {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
margin: var(--space-8);
|
||||
padding: var(--space-8);
|
||||
color: var(--color-grey-400);
|
||||
display: none;
|
||||
z-index: 10;
|
||||
}
|
||||
.favorite[aria-pressed="false"]:hover::before {
|
||||
content: "Ajouter dans mes favoris";
|
||||
background: var(--color-background);
|
||||
padding: 1.3rem 3.5rem 1.25rem 1rem;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
inset: -2px;
|
||||
left: unset;
|
||||
width: max-content;
|
||||
border-radius: var(--rounded-xl);
|
||||
color: var(--color-grey-700);
|
||||
letter-spacing: var(--tracking-wide);
|
||||
font-weight: 500;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.favorite[aria-pressed="true"] {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue