mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 05:25:26 +02:00
parent
a132b83f1b
commit
620465f14c
8 changed files with 312 additions and 528 deletions
|
@ -20,45 +20,33 @@
|
||||||
</v-data-table>
|
</v-data-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import { parseISO, formatDistanceToNow } from "date-fns";
|
import { parseISO, formatDistanceToNow } from "date-fns";
|
||||||
import type { GroupDataExport } from "~/lib/api/types/group";
|
import type { GroupDataExport } from "~/lib/api/types/group";
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
defineProps<{
|
||||||
props: {
|
exports: GroupDataExport[];
|
||||||
exports: {
|
}>();
|
||||||
type: Array as () => GroupDataExport[],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const i18n = useI18n();
|
|
||||||
|
|
||||||
const headers = [
|
const i18n = useI18n();
|
||||||
{ title: i18n.t("export.export"), value: "name" },
|
|
||||||
{ title: i18n.t("export.file-name"), value: "filename" },
|
|
||||||
{ title: i18n.t("export.size"), value: "size" },
|
|
||||||
{ title: i18n.t("export.link-expires"), value: "expires" },
|
|
||||||
{ title: "", value: "actions" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function getTimeToExpire(timeString: string) {
|
const headers = [
|
||||||
const expiresAt = parseISO(timeString);
|
{ title: i18n.t("export.export"), value: "name" },
|
||||||
|
{ title: i18n.t("export.file-name"), value: "filename" },
|
||||||
|
{ title: i18n.t("export.size"), value: "size" },
|
||||||
|
{ title: i18n.t("export.link-expires"), value: "expires" },
|
||||||
|
{ title: "", value: "actions" },
|
||||||
|
];
|
||||||
|
|
||||||
return formatDistanceToNow(expiresAt, {
|
function getTimeToExpire(timeString: string) {
|
||||||
addSuffix: false,
|
const expiresAt = parseISO(timeString);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadData(_: any) {
|
return formatDistanceToNow(expiresAt, {
|
||||||
console.log("Downloading data...");
|
addSuffix: false,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
function downloadData(_: any) {
|
||||||
downloadData,
|
console.log("Downloading data...");
|
||||||
headers,
|
}
|
||||||
getTimeToExpire,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,30 +9,10 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
export default defineNuxtComponent({
|
import type { ReadGroupPreferences } from "~/lib/api/types/user";
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ["update:modelValue"],
|
|
||||||
setup(props, context) {
|
|
||||||
const preferences = computed({
|
|
||||||
get() {
|
|
||||||
return props.modelValue;
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
context.emit("update:modelValue", val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
const preferences = defineModel<ReadGroupPreferences>({ required: true });
|
||||||
preferences,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
<template>
|
|
||||||
<v-select
|
|
||||||
v-model="selected"
|
|
||||||
:items="households"
|
|
||||||
:label="label"
|
|
||||||
:hint="description"
|
|
||||||
:persistent-hint="!!description"
|
|
||||||
item-title="name"
|
|
||||||
:multiple="multiselect"
|
|
||||||
:prepend-inner-icon="$globals.icons.household"
|
|
||||||
return-object
|
|
||||||
>
|
|
||||||
<template #chip="data">
|
|
||||||
<v-chip
|
|
||||||
:key="data.index"
|
|
||||||
class="ma-1"
|
|
||||||
:input-value="data.item"
|
|
||||||
size="small"
|
|
||||||
closable
|
|
||||||
label
|
|
||||||
color="accent"
|
|
||||||
dark
|
|
||||||
@click:close="removeByIndex(data.index)"
|
|
||||||
>
|
|
||||||
{{ data.item.raw.name || data.item }}
|
|
||||||
</v-chip>
|
|
||||||
</template>
|
|
||||||
</v-select>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { useHouseholdStore } from "~/composables/store/use-household-store";
|
|
||||||
|
|
||||||
interface HouseholdLike {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
type: Array as () => HouseholdLike[],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
multiselect: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ["update:modelValue"],
|
|
||||||
setup(props, context) {
|
|
||||||
const selected = computed({
|
|
||||||
get: () => props.modelValue,
|
|
||||||
set: (val) => {
|
|
||||||
context.emit("update:modelValue", val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (selected.value === undefined) {
|
|
||||||
selected.value = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const i18n = useI18n();
|
|
||||||
const label = computed(
|
|
||||||
() => props.multiselect ? i18n.t("household.households") : i18n.t("household.household"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const { store: households } = useHouseholdStore();
|
|
||||||
function removeByIndex(index: number) {
|
|
||||||
if (selected.value === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newSelected = selected.value.filter((_, i) => i !== index);
|
|
||||||
selected.value = [...newSelected];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
selected,
|
|
||||||
label,
|
|
||||||
households,
|
|
||||||
removeByIndex,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -18,7 +18,7 @@
|
||||||
:open-on-hover="mdAndUp"
|
:open-on-hover="mdAndUp"
|
||||||
content-class="d-print-none"
|
content-class="d-print-none"
|
||||||
>
|
>
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props: activatorProps }">
|
||||||
<v-btn
|
<v-btn
|
||||||
:class="{ 'rounded-circle': fab }"
|
:class="{ 'rounded-circle': fab }"
|
||||||
:size="fab ? 'small' : undefined"
|
:size="fab ? 'small' : undefined"
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
:icon="!fab"
|
:icon="!fab"
|
||||||
variant="text"
|
variant="text"
|
||||||
dark
|
dark
|
||||||
v-bind="props"
|
v-bind="activatorProps"
|
||||||
@click.prevent
|
@click.prevent
|
||||||
>
|
>
|
||||||
<v-icon>{{ icon }}</v-icon>
|
<v-icon>{{ icon }}</v-icon>
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Recipe } from "~/lib/api/types/recipe";
|
import type { Recipe } from "~/lib/api/types/recipe";
|
||||||
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
|
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
|
||||||
import type { ShoppingListSummary } from "~/lib/api/types/household";
|
import type { ShoppingListSummary } from "~/lib/api/types/household";
|
||||||
|
@ -64,101 +64,84 @@ export interface ContextMenuItem {
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
interface Props {
|
||||||
components: {
|
recipes?: Recipe[];
|
||||||
RecipeDialogAddToShoppingList,
|
menuTop?: boolean;
|
||||||
},
|
fab?: boolean;
|
||||||
props: {
|
color?: string;
|
||||||
recipes: {
|
menuIcon?: string | null;
|
||||||
type: Array as () => Recipe[],
|
}
|
||||||
default: () => [],
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
},
|
recipes: () => [],
|
||||||
menuTop: {
|
menuTop: true,
|
||||||
type: Boolean,
|
fab: false,
|
||||||
default: true,
|
color: "primary",
|
||||||
},
|
menuIcon: null,
|
||||||
fab: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: "primary",
|
|
||||||
},
|
|
||||||
menuIcon: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props, context) {
|
|
||||||
const { mdAndUp } = useDisplay();
|
|
||||||
|
|
||||||
const i18n = useI18n();
|
|
||||||
const { $globals } = useNuxtApp();
|
|
||||||
const api = useUserApi();
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
loading: false,
|
|
||||||
shoppingListDialog: false,
|
|
||||||
menuItems: [
|
|
||||||
{
|
|
||||||
title: i18n.t("recipe.add-to-list"),
|
|
||||||
icon: $globals.icons.cartCheck,
|
|
||||||
color: undefined,
|
|
||||||
event: "shoppingList",
|
|
||||||
isPublic: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const icon = props.menuIcon || $globals.icons.dotsVertical;
|
|
||||||
|
|
||||||
const shoppingLists = ref<ShoppingListSummary[]>();
|
|
||||||
const recipesWithScales = computed(() => {
|
|
||||||
return props.recipes.map((recipe) => {
|
|
||||||
return {
|
|
||||||
scale: 1,
|
|
||||||
...recipe,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
async function getShoppingLists() {
|
|
||||||
const { data } = await api.shopping.lists.getAll(1, -1, { orderBy: "name", orderDirection: "asc" });
|
|
||||||
if (data) {
|
|
||||||
shoppingLists.value = data.items as ShoppingListSummary[] ?? [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
||||||
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
|
|
||||||
shoppingList: () => {
|
|
||||||
getShoppingLists();
|
|
||||||
state.shoppingListDialog = true;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function contextMenuEventHandler(eventKey: string) {
|
|
||||||
const handler = eventHandlers[eventKey];
|
|
||||||
|
|
||||||
if (handler && typeof handler === "function") {
|
|
||||||
handler();
|
|
||||||
state.loading = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.emit(eventKey);
|
|
||||||
state.loading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
contextMenuEventHandler,
|
|
||||||
icon,
|
|
||||||
recipesWithScales,
|
|
||||||
shoppingLists,
|
|
||||||
mdAndUp,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
[key: string]: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { mdAndUp } = useDisplay();
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
const { $globals } = useNuxtApp();
|
||||||
|
const api = useUserApi();
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
loading: false,
|
||||||
|
shoppingListDialog: false,
|
||||||
|
menuItems: [
|
||||||
|
{
|
||||||
|
title: i18n.t("recipe.add-to-list"),
|
||||||
|
icon: $globals.icons.cartCheck,
|
||||||
|
color: undefined,
|
||||||
|
event: "shoppingList",
|
||||||
|
isPublic: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { shoppingListDialog, menuItems } = toRefs(state);
|
||||||
|
|
||||||
|
const icon = props.menuIcon || $globals.icons.dotsVertical;
|
||||||
|
|
||||||
|
const shoppingLists = ref<ShoppingListSummary[]>();
|
||||||
|
const recipesWithScales = computed(() => {
|
||||||
|
return props.recipes.map((recipe) => {
|
||||||
|
return {
|
||||||
|
scale: 1,
|
||||||
|
...recipe,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getShoppingLists() {
|
||||||
|
const { data } = await api.shopping.lists.getAll(1, -1, { orderBy: "name", orderDirection: "asc" });
|
||||||
|
if (data) {
|
||||||
|
shoppingLists.value = data.items as ShoppingListSummary[] ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||||
|
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
|
||||||
|
shoppingList: () => {
|
||||||
|
getShoppingLists();
|
||||||
|
state.shoppingListDialog = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function contextMenuEventHandler(eventKey: string) {
|
||||||
|
const handler = eventHandlers[eventKey];
|
||||||
|
|
||||||
|
if (handler && typeof handler === "function") {
|
||||||
|
handler();
|
||||||
|
state.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(eventKey);
|
||||||
|
state.loading = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
style="gap: 10px"
|
style="gap: 10px"
|
||||||
>
|
>
|
||||||
<v-select
|
<v-select
|
||||||
v-model="inputDay"
|
v-model="day"
|
||||||
:items="MEAL_DAY_OPTIONS"
|
:items="MEAL_DAY_OPTIONS"
|
||||||
:label="$t('meal-plan.rule-day')"
|
:label="$t('meal-plan.rule-day')"
|
||||||
/>
|
/>
|
||||||
<v-select
|
<v-select
|
||||||
v-model="inputEntryType"
|
v-model="entryType"
|
||||||
:items="MEAL_TYPE_OPTIONS"
|
:items="MEAL_TYPE_OPTIONS"
|
||||||
:label="$t('meal-plan.meal-type')"
|
:label="$t('meal-plan.meal-type')"
|
||||||
/>
|
/>
|
||||||
|
@ -19,157 +19,104 @@
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<QueryFilterBuilder
|
<QueryFilterBuilder
|
||||||
:field-defs="fieldDefs"
|
:field-defs="fieldDefs"
|
||||||
:initial-query-filter="queryFilter"
|
:initial-query-filter="props.queryFilter"
|
||||||
@input="handleQueryFilterInput"
|
@input="handleQueryFilterInput"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO: proper pluralization of inputDay -->
|
<!-- TODO: proper pluralization of inputDay -->
|
||||||
{{ $t('meal-plan.this-rule-will-apply', {
|
{{ $t('meal-plan.this-rule-will-apply', {
|
||||||
dayCriteria: inputDay === "unset" ? $t('meal-plan.to-all-days') : $t('meal-plan.on-days', [inputDay]),
|
dayCriteria: day === "unset" ? $t('meal-plan.to-all-days') : $t('meal-plan.on-days', [day]),
|
||||||
mealTypeCriteria: inputEntryType === "unset" ? $t('meal-plan.for-all-meal-types') : $t('meal-plan.for-type-meal-types', [inputEntryType]),
|
mealTypeCriteria: entryType === "unset" ? $t('meal-plan.for-all-meal-types') : $t('meal-plan.for-type-meal-types', [entryType]),
|
||||||
}) }}
|
}) }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
|
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
|
||||||
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
|
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
|
||||||
import { Organizer } from "~/lib/api/types/non-generated";
|
import { Organizer } from "~/lib/api/types/non-generated";
|
||||||
import type { QueryFilterJSON } from "~/lib/api/types/response";
|
import type { QueryFilterJSON } from "~/lib/api/types/response";
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
interface Props {
|
||||||
components: {
|
queryFilter?: QueryFilterJSON | null;
|
||||||
QueryFilterBuilder,
|
showHelp?: boolean;
|
||||||
},
|
}
|
||||||
props: {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
day: {
|
queryFilter: null,
|
||||||
type: String,
|
showHelp: false,
|
||||||
default: "unset",
|
|
||||||
},
|
|
||||||
entryType: {
|
|
||||||
type: String,
|
|
||||||
default: "unset",
|
|
||||||
},
|
|
||||||
queryFilterString: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
queryFilter: {
|
|
||||||
type: Object as () => QueryFilterJSON,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
showHelp: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ["update:day", "update:entry-type", "update:query-filter-string"],
|
|
||||||
setup(props, context) {
|
|
||||||
const i18n = useI18n();
|
|
||||||
|
|
||||||
const MEAL_TYPE_OPTIONS = [
|
|
||||||
{ title: i18n.t("meal-plan.breakfast"), value: "breakfast" },
|
|
||||||
{ title: i18n.t("meal-plan.lunch"), value: "lunch" },
|
|
||||||
{ title: i18n.t("meal-plan.dinner"), value: "dinner" },
|
|
||||||
{ title: i18n.t("meal-plan.side"), value: "side" },
|
|
||||||
{ title: i18n.t("meal-plan.type-any"), value: "unset" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const MEAL_DAY_OPTIONS = [
|
|
||||||
{ title: i18n.t("general.monday"), value: "monday" },
|
|
||||||
{ title: i18n.t("general.tuesday"), value: "tuesday" },
|
|
||||||
{ title: i18n.t("general.wednesday"), value: "wednesday" },
|
|
||||||
{ title: i18n.t("general.thursday"), value: "thursday" },
|
|
||||||
{ title: i18n.t("general.friday"), value: "friday" },
|
|
||||||
{ title: i18n.t("general.saturday"), value: "saturday" },
|
|
||||||
{ title: i18n.t("general.sunday"), value: "sunday" },
|
|
||||||
{ title: i18n.t("meal-plan.day-any"), value: "unset" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const inputDay = computed({
|
|
||||||
get: () => {
|
|
||||||
return props.day;
|
|
||||||
},
|
|
||||||
set: (val) => {
|
|
||||||
context.emit("update:day", val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const inputEntryType = computed({
|
|
||||||
get: () => {
|
|
||||||
return props.entryType;
|
|
||||||
},
|
|
||||||
set: (val) => {
|
|
||||||
context.emit("update:entry-type", val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const inputQueryFilterString = computed({
|
|
||||||
get: () => {
|
|
||||||
return props.queryFilterString;
|
|
||||||
},
|
|
||||||
set: (val) => {
|
|
||||||
context.emit("update:query-filter-string", val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleQueryFilterInput(value: string | undefined) {
|
|
||||||
inputQueryFilterString.value = value || "";
|
|
||||||
};
|
|
||||||
|
|
||||||
const fieldDefs: FieldDefinition[] = [
|
|
||||||
{
|
|
||||||
name: "recipe_category.id",
|
|
||||||
label: i18n.t("category.categories"),
|
|
||||||
type: Organizer.Category,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "tags.id",
|
|
||||||
label: i18n.t("tag.tags"),
|
|
||||||
type: Organizer.Tag,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "recipe_ingredient.food.id",
|
|
||||||
label: i18n.t("recipe.ingredients"),
|
|
||||||
type: Organizer.Food,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "tools.id",
|
|
||||||
label: i18n.t("tool.tools"),
|
|
||||||
type: Organizer.Tool,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "household_id",
|
|
||||||
label: i18n.t("household.households"),
|
|
||||||
type: Organizer.Household,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "last_made",
|
|
||||||
label: i18n.t("general.last-made"),
|
|
||||||
type: "date",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "created_at",
|
|
||||||
label: i18n.t("general.date-created"),
|
|
||||||
type: "date",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "updated_at",
|
|
||||||
label: i18n.t("general.date-updated"),
|
|
||||||
type: "date",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
MEAL_TYPE_OPTIONS,
|
|
||||||
MEAL_DAY_OPTIONS,
|
|
||||||
inputDay,
|
|
||||||
inputEntryType,
|
|
||||||
inputQueryFilterString,
|
|
||||||
handleQueryFilterInput,
|
|
||||||
fieldDefs,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const day = defineModel<string>("day", { default: "unset" });
|
||||||
|
const entryType = defineModel<string>("entryType", { default: "unset" });
|
||||||
|
const queryFilterString = defineModel<string>("queryFilterString", { default: "" });
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
|
||||||
|
const MEAL_TYPE_OPTIONS = [
|
||||||
|
{ title: i18n.t("meal-plan.breakfast"), value: "breakfast" },
|
||||||
|
{ title: i18n.t("meal-plan.lunch"), value: "lunch" },
|
||||||
|
{ title: i18n.t("meal-plan.dinner"), value: "dinner" },
|
||||||
|
{ title: i18n.t("meal-plan.side"), value: "side" },
|
||||||
|
{ title: i18n.t("meal-plan.type-any"), value: "unset" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const MEAL_DAY_OPTIONS = [
|
||||||
|
{ title: i18n.t("general.monday"), value: "monday" },
|
||||||
|
{ title: i18n.t("general.tuesday"), value: "tuesday" },
|
||||||
|
{ title: i18n.t("general.wednesday"), value: "wednesday" },
|
||||||
|
{ title: i18n.t("general.thursday"), value: "thursday" },
|
||||||
|
{ title: i18n.t("general.friday"), value: "friday" },
|
||||||
|
{ title: i18n.t("general.saturday"), value: "saturday" },
|
||||||
|
{ title: i18n.t("general.sunday"), value: "sunday" },
|
||||||
|
{ title: i18n.t("meal-plan.day-any"), value: "unset" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function handleQueryFilterInput(value: string | undefined) {
|
||||||
|
console.warn("handleQueryFilterInput called with value:", value);
|
||||||
|
queryFilterString.value = value || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldDefs: FieldDefinition[] = [
|
||||||
|
{
|
||||||
|
name: "recipe_category.id",
|
||||||
|
label: i18n.t("category.categories"),
|
||||||
|
type: Organizer.Category,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tags.id",
|
||||||
|
label: i18n.t("tag.tags"),
|
||||||
|
type: Organizer.Tag,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "recipe_ingredient.food.id",
|
||||||
|
label: i18n.t("recipe.ingredients"),
|
||||||
|
type: Organizer.Food,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tools.id",
|
||||||
|
label: i18n.t("tool.tools"),
|
||||||
|
type: Organizer.Tool,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "household_id",
|
||||||
|
label: i18n.t("household.households"),
|
||||||
|
type: Organizer.Household,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "last_made",
|
||||||
|
label: i18n.t("general.last-made"),
|
||||||
|
type: "date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "created_at",
|
||||||
|
label: i18n.t("general.date-created"),
|
||||||
|
type: "date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "updated_at",
|
||||||
|
label: i18n.t("general.date-updated"),
|
||||||
|
type: "date",
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
:label="$t('settings.webhooks.webhook-url')"
|
:label="$t('settings.webhooks.webhook-url')"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
/>
|
/>
|
||||||
<v-time-picker
|
<v-text-field
|
||||||
v-model="scheduledTime"
|
v-model="scheduledTime"
|
||||||
class="elevation-2"
|
type="time"
|
||||||
ampm-in-title
|
clearable
|
||||||
format="ampm"
|
variant="underlined"
|
||||||
/>
|
/>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions class="py-0 justify-end">
|
<v-card-actions class="py-0 justify-end">
|
||||||
|
@ -50,52 +50,43 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ReadWebhook } from "~/lib/api/types/household";
|
import type { ReadWebhook } from "~/lib/api/types/household";
|
||||||
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
|
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
const props = defineProps<{
|
||||||
props: {
|
webhook: ReadWebhook;
|
||||||
webhook: {
|
}>();
|
||||||
type: Object as () => ReadWebhook,
|
|
||||||
required: true,
|
const emit = defineEmits<{
|
||||||
},
|
delete: [id: string];
|
||||||
|
save: [webhook: ReadWebhook];
|
||||||
|
test: [id: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
const itemUTC = ref<string>(props.webhook.scheduledTime);
|
||||||
|
const itemLocal = ref<string>(timeUTCToLocal(props.webhook.scheduledTime));
|
||||||
|
|
||||||
|
const scheduledTime = computed({
|
||||||
|
get() {
|
||||||
|
return itemLocal.value;
|
||||||
},
|
},
|
||||||
emits: ["delete", "save", "test"],
|
set(v: string) {
|
||||||
setup(props, { emit }) {
|
itemUTC.value = timeLocalToUTC(v);
|
||||||
const i18n = useI18n();
|
itemLocal.value = v;
|
||||||
const itemUTC = ref<string>(props.webhook.scheduledTime);
|
|
||||||
const itemLocal = ref<string>(timeUTCToLocal(props.webhook.scheduledTime));
|
|
||||||
|
|
||||||
const scheduledTime = computed({
|
|
||||||
get() {
|
|
||||||
return itemLocal.value;
|
|
||||||
},
|
|
||||||
set(v: string) {
|
|
||||||
itemUTC.value = timeLocalToUTC(v);
|
|
||||||
itemLocal.value = v;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const webhookCopy = ref({ ...props.webhook });
|
|
||||||
|
|
||||||
function handleSave() {
|
|
||||||
webhookCopy.value.scheduledTime = itemLocal.value;
|
|
||||||
emit("save", webhookCopy.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set page title using useSeoMeta
|
|
||||||
useSeoMeta({
|
|
||||||
title: i18n.t("settings.webhooks.webhooks"),
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
webhookCopy,
|
|
||||||
scheduledTime,
|
|
||||||
handleSave,
|
|
||||||
itemUTC,
|
|
||||||
itemLocal,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const webhookCopy = ref({ ...props.webhook });
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
webhookCopy.value.scheduledTime = itemLocal.value;
|
||||||
|
emit("save", webhookCopy.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set page title using useSeoMeta
|
||||||
|
useSeoMeta({
|
||||||
|
title: i18n.t("settings.webhooks.webhooks"),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -41,106 +41,81 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ReadHouseholdPreferences } from "~/lib/api/types/household";
|
import type { ReadHouseholdPreferences } from "~/lib/api/types/household";
|
||||||
|
|
||||||
export default defineNuxtComponent({
|
const preferences = defineModel<ReadHouseholdPreferences>({ required: true });
|
||||||
props: {
|
const i18n = useI18n();
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
type Preference = {
|
||||||
required: true,
|
key: keyof ReadHouseholdPreferences;
|
||||||
},
|
label: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const recipePreferences: Preference[] = [
|
||||||
|
{
|
||||||
|
key: "recipePublic",
|
||||||
|
label: i18n.t("group.allow-users-outside-of-your-group-to-see-your-recipes"),
|
||||||
|
description: i18n.t("group.allow-users-outside-of-your-group-to-see-your-recipes-description"),
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue"],
|
{
|
||||||
setup(props, context) {
|
key: "recipeShowNutrition",
|
||||||
const i18n = useI18n();
|
label: i18n.t("group.show-nutrition-information"),
|
||||||
|
description: i18n.t("group.show-nutrition-information-description"),
|
||||||
type Preference = {
|
|
||||||
key: keyof ReadHouseholdPreferences;
|
|
||||||
label: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const recipePreferences: Preference[] = [
|
|
||||||
{
|
|
||||||
key: "recipePublic",
|
|
||||||
label: i18n.t("group.allow-users-outside-of-your-group-to-see-your-recipes"),
|
|
||||||
description: i18n.t("group.allow-users-outside-of-your-group-to-see-your-recipes-description"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "recipeShowNutrition",
|
|
||||||
label: i18n.t("group.show-nutrition-information"),
|
|
||||||
description: i18n.t("group.show-nutrition-information-description"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "recipeShowAssets",
|
|
||||||
label: i18n.t("group.show-recipe-assets"),
|
|
||||||
description: i18n.t("group.show-recipe-assets-description"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "recipeLandscapeView",
|
|
||||||
label: i18n.t("group.default-to-landscape-view"),
|
|
||||||
description: i18n.t("group.default-to-landscape-view-description"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "recipeDisableComments",
|
|
||||||
label: i18n.t("group.disable-users-from-commenting-on-recipes"),
|
|
||||||
description: i18n.t("group.disable-users-from-commenting-on-recipes-description"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "recipeDisableAmount",
|
|
||||||
label: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food"),
|
|
||||||
description: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food-description"),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const allDays = [
|
|
||||||
{
|
|
||||||
name: i18n.t("general.sunday"),
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.t("general.monday"),
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.t("general.tuesday"),
|
|
||||||
value: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.t("general.wednesday"),
|
|
||||||
value: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.t("general.thursday"),
|
|
||||||
value: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.t("general.friday"),
|
|
||||||
value: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n.t("general.saturday"),
|
|
||||||
value: 6,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const preferences = computed({
|
|
||||||
get() {
|
|
||||||
return props.modelValue;
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
context.emit("update:modelValue", val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
allDays,
|
|
||||||
preferences,
|
|
||||||
recipePreferences,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
key: "recipeShowAssets",
|
||||||
|
label: i18n.t("group.show-recipe-assets"),
|
||||||
|
description: i18n.t("group.show-recipe-assets-description"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "recipeLandscapeView",
|
||||||
|
label: i18n.t("group.default-to-landscape-view"),
|
||||||
|
description: i18n.t("group.default-to-landscape-view-description"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "recipeDisableComments",
|
||||||
|
label: i18n.t("group.disable-users-from-commenting-on-recipes"),
|
||||||
|
description: i18n.t("group.disable-users-from-commenting-on-recipes-description"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "recipeDisableAmount",
|
||||||
|
label: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food"),
|
||||||
|
description: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food-description"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const allDays = [
|
||||||
|
{
|
||||||
|
name: i18n.t("general.sunday"),
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("general.monday"),
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("general.tuesday"),
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("general.wednesday"),
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("general.thursday"),
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("general.friday"),
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("general.saturday"),
|
||||||
|
value: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
:model-value="field.value"
|
:model-value="field.value"
|
||||||
type="number"
|
type="number"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
@:model-value="setFieldValue(field, index, $event)"
|
@update:model-value="setFieldValue(field, index, $event)"
|
||||||
/>
|
/>
|
||||||
<v-checkbox
|
<v-checkbox
|
||||||
v-else-if="field.type === 'boolean'"
|
v-else-if="field.type === 'boolean'"
|
||||||
|
@ -190,6 +190,7 @@
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
|
@update:model-value="setFieldOrganizers(field, index, $event)"
|
||||||
/>
|
/>
|
||||||
<RecipeOrganizerSelector
|
<RecipeOrganizerSelector
|
||||||
v-else-if="field.type === Organizer.Tag"
|
v-else-if="field.type === Organizer.Tag"
|
||||||
|
@ -199,6 +200,7 @@
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
|
@update:model-value="setFieldOrganizers(field, index, $event)"
|
||||||
/>
|
/>
|
||||||
<RecipeOrganizerSelector
|
<RecipeOrganizerSelector
|
||||||
v-else-if="field.type === Organizer.Tool"
|
v-else-if="field.type === Organizer.Tool"
|
||||||
|
@ -208,6 +210,7 @@
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
|
@update:model-value="setFieldOrganizers(field, index, $event)"
|
||||||
/>
|
/>
|
||||||
<RecipeOrganizerSelector
|
<RecipeOrganizerSelector
|
||||||
v-else-if="field.type === Organizer.Food"
|
v-else-if="field.type === Organizer.Food"
|
||||||
|
@ -217,6 +220,7 @@
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
|
@update:model-value="setFieldOrganizers(field, index, $event)"
|
||||||
/>
|
/>
|
||||||
<RecipeOrganizerSelector
|
<RecipeOrganizerSelector
|
||||||
v-else-if="field.type === Organizer.Household"
|
v-else-if="field.type === Organizer.Household"
|
||||||
|
@ -226,6 +230,7 @@
|
||||||
:show-label="false"
|
:show-label="false"
|
||||||
:show-icon="false"
|
:show-icon="false"
|
||||||
variant="underlined"
|
variant="underlined"
|
||||||
|
@update:model-value="setFieldOrganizers(field, index, $event)"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
<!-- right parenthesis -->
|
<!-- right parenthesis -->
|
||||||
|
@ -414,6 +419,12 @@ function setFieldValues(field: FieldWithId, index: number, values: FieldValue[])
|
||||||
fields.value[index].values = values;
|
fields.value[index].values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFieldOrganizers(field: FieldWithId, index: number, organizers: OrganizerBase[]) {
|
||||||
|
fields.value[index].organizers = organizers;
|
||||||
|
// Sync the values array with the organizers array
|
||||||
|
fields.value[index].values = organizers.map(org => org.id?.toString() || "").filter(id => id);
|
||||||
|
}
|
||||||
|
|
||||||
function removeField(index: number) {
|
function removeField(index: number) {
|
||||||
fields.value.splice(index, 1);
|
fields.value.splice(index, 1);
|
||||||
state.datePickers.splice(index, 1);
|
state.datePickers.splice(index, 1);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue