mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
Organizers Page
This commit is contained in:
parent
00fab04fee
commit
48d836beeb
1 changed files with 121 additions and 150 deletions
|
@ -122,9 +122,8 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
|
|
||||||
import { useContextPresets } from "~/composables/use-context-presents";
|
import { useContextPresets } from "~/composables/use-context-presents";
|
||||||
import RecipeOrganizerDialog from "~/components/Domain/Recipe/RecipeOrganizerDialog.vue";
|
import RecipeOrganizerDialog from "~/components/Domain/Recipe/RecipeOrganizerDialog.vue";
|
||||||
import { Organizer, type RecipeOrganizer } from "~/lib/api/types/non-generated";
|
import { Organizer, type RecipeOrganizer } from "~/lib/api/types/non-generated";
|
||||||
|
@ -138,156 +137,128 @@ interface GenericItem {
|
||||||
onHand: boolean;
|
onHand: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
const props = defineProps<{
|
||||||
components: {
|
items: GenericItem[];
|
||||||
RecipeOrganizerDialog,
|
icon: string;
|
||||||
},
|
itemType: RecipeOrganizer;
|
||||||
props: {
|
}>();
|
||||||
items: {
|
|
||||||
type: Array as () => GenericItem[],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
itemType: {
|
|
||||||
type: String as () => RecipeOrganizer,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ["update", "delete"],
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const state = reactive({
|
|
||||||
// Search Options
|
|
||||||
options: {
|
|
||||||
ignoreLocation: true,
|
|
||||||
shouldSort: true,
|
|
||||||
threshold: 0.2,
|
|
||||||
location: 0,
|
|
||||||
distance: 20,
|
|
||||||
findAllMatches: true,
|
|
||||||
maxPatternLength: 32,
|
|
||||||
minMatchCharLength: 1,
|
|
||||||
keys: ["name"],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const $auth = useMealieAuth();
|
const emit = defineEmits<{
|
||||||
const route = useRoute();
|
update: [item: GenericItem];
|
||||||
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user?.value?.groupSlug || "");
|
delete: [id: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
// =================================================================
|
const state = reactive({
|
||||||
// Context Menu
|
// Search Options
|
||||||
|
options: {
|
||||||
const dialogs = ref({
|
ignoreLocation: true,
|
||||||
organizer: false,
|
shouldSort: true,
|
||||||
update: false,
|
threshold: 0.2,
|
||||||
delete: false,
|
location: 0,
|
||||||
});
|
distance: 20,
|
||||||
|
findAllMatches: true,
|
||||||
const presets = useContextPresets();
|
maxPatternLength: 32,
|
||||||
|
minMatchCharLength: 1,
|
||||||
const translationKey = computed<string>(() => {
|
keys: ["name"],
|
||||||
const typeMap = {
|
|
||||||
categories: "category.category",
|
|
||||||
tags: "tag.tag",
|
|
||||||
tools: "tool.tool",
|
|
||||||
foods: "shopping-list.food",
|
|
||||||
households: "household.household",
|
|
||||||
};
|
|
||||||
return typeMap[props.itemType] || "";
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteTarget = ref<GenericItem | null>(null);
|
|
||||||
const updateTarget = ref<GenericItem | null>(null);
|
|
||||||
|
|
||||||
function confirmDelete(item: GenericItem) {
|
|
||||||
deleteTarget.value = item;
|
|
||||||
dialogs.value.delete = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteOne() {
|
|
||||||
if (!deleteTarget.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit("delete", deleteTarget.value.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openUpdateDialog(item: GenericItem) {
|
|
||||||
updateTarget.value = deepCopy(item);
|
|
||||||
dialogs.value.update = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateOne() {
|
|
||||||
if (!updateTarget.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit("update", updateTarget.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================================================
|
|
||||||
// Search Functions
|
|
||||||
|
|
||||||
const searchString = useRouteQuery("q", "");
|
|
||||||
|
|
||||||
const fuse = computed(() => {
|
|
||||||
return new Fuse(props.items, state.options);
|
|
||||||
});
|
|
||||||
|
|
||||||
const fuzzyItems = computed<GenericItem[]>(() => {
|
|
||||||
if (searchString.value.trim() === "") {
|
|
||||||
return props.items;
|
|
||||||
}
|
|
||||||
const result = fuse.value.search(searchString.value.trim() as string);
|
|
||||||
return result.map(x => x.item);
|
|
||||||
});
|
|
||||||
|
|
||||||
// =================================================================
|
|
||||||
// Sorted Items
|
|
||||||
|
|
||||||
const itemsSorted = computed(() => {
|
|
||||||
const byLetter: { [key: string]: Array<GenericItem> } = {};
|
|
||||||
|
|
||||||
if (!fuzzyItems.value) {
|
|
||||||
return byLetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
[...fuzzyItems.value]
|
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
|
||||||
.forEach((item) => {
|
|
||||||
const letter = item.name[0].toUpperCase();
|
|
||||||
if (!byLetter[letter]) {
|
|
||||||
byLetter[letter] = [];
|
|
||||||
}
|
|
||||||
byLetter[letter].push(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
return byLetter;
|
|
||||||
});
|
|
||||||
|
|
||||||
function isTitle(str: number | string) {
|
|
||||||
return typeof str === "string" && str.length === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
groupSlug,
|
|
||||||
isTitle,
|
|
||||||
dialogs,
|
|
||||||
confirmDelete,
|
|
||||||
openUpdateDialog,
|
|
||||||
updateOne,
|
|
||||||
updateTarget,
|
|
||||||
deleteOne,
|
|
||||||
deleteTarget,
|
|
||||||
Organizer,
|
|
||||||
presets,
|
|
||||||
itemsSorted,
|
|
||||||
searchString,
|
|
||||||
translationKey,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $auth = useMealieAuth();
|
||||||
|
const route = useRoute();
|
||||||
|
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user?.value?.groupSlug || "");
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Context Menu
|
||||||
|
|
||||||
|
const dialogs = ref({
|
||||||
|
organizer: false,
|
||||||
|
update: false,
|
||||||
|
delete: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const presets = useContextPresets();
|
||||||
|
|
||||||
|
const translationKey = computed<string>(() => {
|
||||||
|
const typeMap = {
|
||||||
|
categories: "category.category",
|
||||||
|
tags: "tag.tag",
|
||||||
|
tools: "tool.tool",
|
||||||
|
foods: "shopping-list.food",
|
||||||
|
households: "household.household",
|
||||||
|
};
|
||||||
|
return typeMap[props.itemType] || "";
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteTarget = ref<GenericItem | null>(null);
|
||||||
|
const updateTarget = ref<GenericItem | null>(null);
|
||||||
|
|
||||||
|
function confirmDelete(item: GenericItem) {
|
||||||
|
deleteTarget.value = item;
|
||||||
|
dialogs.value.delete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteOne() {
|
||||||
|
if (!deleteTarget.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit("delete", deleteTarget.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openUpdateDialog(item: GenericItem) {
|
||||||
|
updateTarget.value = deepCopy(item);
|
||||||
|
dialogs.value.update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOne() {
|
||||||
|
if (!updateTarget.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit("update", updateTarget.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// Search Functions
|
||||||
|
|
||||||
|
const searchString = useRouteQuery("q", "");
|
||||||
|
|
||||||
|
const fuse = computed(() => {
|
||||||
|
return new Fuse(props.items, state.options);
|
||||||
|
});
|
||||||
|
|
||||||
|
const fuzzyItems = computed<GenericItem[]>(() => {
|
||||||
|
if (searchString.value.trim() === "") {
|
||||||
|
return props.items;
|
||||||
|
}
|
||||||
|
const result = fuse.value.search(searchString.value.trim() as string);
|
||||||
|
return result.map(x => x.item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Sorted Items
|
||||||
|
|
||||||
|
const itemsSorted = computed(() => {
|
||||||
|
const byLetter: { [key: string]: Array<GenericItem> } = {};
|
||||||
|
|
||||||
|
if (!fuzzyItems.value) {
|
||||||
|
return byLetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
[...fuzzyItems.value]
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
.forEach((item) => {
|
||||||
|
const letter = item.name[0].toUpperCase();
|
||||||
|
if (!byLetter[letter]) {
|
||||||
|
byLetter[letter] = [];
|
||||||
|
}
|
||||||
|
byLetter[letter].push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
return byLetter;
|
||||||
|
});
|
||||||
|
|
||||||
|
function isTitle(str: number | string) {
|
||||||
|
return typeof str === "string" && str.length === 1;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue