feat: ajout de la gestion des jobs avec création, récupération, suppression et filtrage via l'API, incluant des entités, des composants Vue.js et des mises à jour de la documentation API
This commit is contained in:
parent
4d1d5b9f21
commit
fd2d3cd640
7
assets/vue/app/shared/components/ui/Divider.vue
Normal file
7
assets/vue/app/shared/components/ui/Divider.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="min-h-14 mx-4 border-r opacity-50 border-green-500"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Pas de script nécessaire pour ce composant purement visuel
|
||||
</script>
|
||||
@@ -1,47 +1,26 @@
|
||||
<template>
|
||||
<div :class="['bg-white shadow-sm px-4 py-2', $attrs.class]">
|
||||
<div class="container mx-auto flex justify-between items-center">
|
||||
<!-- Left section -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-for="(item, index) in config.leftSection"
|
||||
:key="index"
|
||||
@click="item.onClick"
|
||||
:class="[
|
||||
'p-2 rounded-lg transition-colors',
|
||||
item.active
|
||||
? 'bg-green-100 text-green-600'
|
||||
: 'hover:bg-gray-100'
|
||||
]"
|
||||
>
|
||||
<component :is="item.icon" class="h-5 w-5" />
|
||||
<span v-if="item.label" class="ml-2">{{ item.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div :class="['bg-gray-800 p-3 min-h-14', $attrs.class]">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<!-- Left section -->
|
||||
<ToolbarSection :items="config.leftSection" />
|
||||
|
||||
<!-- Right section -->
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-for="(item, index) in config.rightSection"
|
||||
:key="index"
|
||||
@click="item.onClick"
|
||||
class="p-2 rounded-lg hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<component :is="item.icon" class="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Right section -->
|
||||
<ToolbarSection :items="config.rightSection" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator: (value) => {
|
||||
return value.leftSection && value.rightSection;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
import ToolbarSection from './ToolbarSection.vue';
|
||||
|
||||
defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator: value => {
|
||||
// Vérifie que leftSection et rightSection sont des tableaux
|
||||
return Array.isArray(value.leftSection) && Array.isArray(value.rightSection);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
37
assets/vue/app/shared/components/ui/ToolbarButton.vue
Normal file
37
assets/vue/app/shared/components/ui/ToolbarButton.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<button
|
||||
@click="$emit('click')"
|
||||
:class="[
|
||||
'flex flex-col items-center justify-center p-1 rounded group text-white min-w-[50px]',
|
||||
active
|
||||
? 'text-green-500' // Style actif
|
||||
: 'hover:text-green-500' // Effet de survol
|
||||
]"
|
||||
:aria-label="label || 'Toolbar button'">
|
||||
<component v-if="icon" :is="icon" class="h-6 w-6 mb-1" />
|
||||
<span v-if="label" class="text-xs">{{ label }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
defineProps({
|
||||
icon: {
|
||||
type: [Object, Function], // Heroicons sont souvent importés comme des objets/fonctions
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
defineEmits(['click']);
|
||||
</script>
|
||||
67
assets/vue/app/shared/components/ui/ToolbarDropdown.vue
Normal file
67
assets/vue/app/shared/components/ui/ToolbarDropdown.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<Menu as="div" class="relative inline-block">
|
||||
<div>
|
||||
<MenuButton
|
||||
:class="[
|
||||
'flex flex-col items-center justify-center p-1 rounded group text-white w-20 hover:text-green-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75',
|
||||
active ? 'text-green-500' : ''
|
||||
]"
|
||||
:aria-label="label || 'Options'">
|
||||
<component v-if="icon" :is="icon" class="h-6 w-6 mb-1" aria-hidden="true" />
|
||||
<span v-if="label" class="text-xs truncate w-full px-1">{{ label }}</span>
|
||||
</MenuButton>
|
||||
</div>
|
||||
|
||||
<MenuItems
|
||||
class="absolute left-0 mt-2 w-max origin-top-left rounded-sm bg-gray-800 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
<div class="px-1 py-1">
|
||||
<MenuItem v-for="(item, index) in items" :key="index" v-slot="{ active }" :disabled="item.disabled">
|
||||
<button
|
||||
:class="[
|
||||
item.isSelected ? 'text-green-500' : active ? 'text-green-500' : 'text-white',
|
||||
'group flex w-full items-center rounded-sm px-3 py-2 text-sm',
|
||||
item.disabled ? 'opacity-50 cursor-not-allowed' : ''
|
||||
]"
|
||||
@click="item.onClick">
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue';
|
||||
|
||||
defineProps({
|
||||
icon: {
|
||||
type: [Object, Function],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: items => {
|
||||
return items.every(
|
||||
item =>
|
||||
item &&
|
||||
item.label &&
|
||||
typeof item.onClick === 'function' &&
|
||||
(item.isSelected === undefined || typeof item.isSelected === 'boolean')
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
44
assets/vue/app/shared/components/ui/ToolbarSection.vue
Normal file
44
assets/vue/app/shared/components/ui/ToolbarSection.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="flex items-center space-x-2">
|
||||
<template v-for="(item, index) in items" :key="index">
|
||||
<ToolbarButton
|
||||
v-if="item.type === 'button'"
|
||||
:icon="item.icon"
|
||||
:label="item.label"
|
||||
:active="item.active"
|
||||
@click="item.onClick" />
|
||||
<ToolbarDropdown
|
||||
v-else-if="item.type === 'dropdown'"
|
||||
:icon="item.icon"
|
||||
:label="item.label"
|
||||
:active="item.active"
|
||||
:items="item.items" />
|
||||
<Divider v-else-if="item.type === 'divider'" />
|
||||
<!-- Ajoutez d'autres types d'éléments ici si nécessaire -->
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ToolbarButton from './ToolbarButton.vue';
|
||||
import ToolbarDropdown from './ToolbarDropdown.vue';
|
||||
import Divider from './Divider.vue';
|
||||
|
||||
defineProps({
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
validator: items => {
|
||||
return items.every(
|
||||
item =>
|
||||
item &&
|
||||
item.type &&
|
||||
(item.type === 'button' ||
|
||||
item.type === 'divider' ||
|
||||
(item.type === 'dropdown' && Array.isArray(item.items)))
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user