Merge branch 'mealie-next' into feat/standardize-units

This commit is contained in:
Michael Genson
2026-02-26 16:53:15 +00:00
19 changed files with 2429 additions and 2340 deletions

View File

@@ -2,9 +2,11 @@
<!-- Create Dialog --> <!-- Create Dialog -->
<BaseDialog <BaseDialog
v-model="createDialog" v-model="createDialog"
:title="$t('general.create')" :title="createTitle || $t('general.create')"
:icon="icon" :icon="icon"
color="primary" color="primary"
max-width="600px"
width="100%"
:submit-disabled="!createFormValid" :submit-disabled="!createFormValid"
can-confirm can-confirm
@confirm="emit('create-one', createForm.data)" @confirm="emit('create-one', createForm.data)"
@@ -15,6 +17,7 @@
v-model="createForm.data" v-model="createForm.data"
v-model:is-valid="createFormValid" v-model:is-valid="createFormValid"
:items="createForm.items" :items="createForm.items"
class="py-2"
/> />
</div> </div>
</BaseDialog> </BaseDialog>
@@ -22,18 +25,22 @@
<!-- Edit Dialog --> <!-- Edit Dialog -->
<BaseDialog <BaseDialog
v-model="editDialog" v-model="editDialog"
:title="$t('general.edit')" :title="editTitle || $t('general.edit')"
:icon="icon" :icon="icon"
color="primary" color="primary"
max-width="600px"
width="100%"
:submit-disabled="!editFormValid" :submit-disabled="!editFormValid"
can-confirm can-confirm
@confirm="emit('edit-one', editForm.data)" @confirm="emit('edit-one', editForm.data)"
> >
<div class="mx-2 mt-2"> <div class="mx-2 mt-2">
<slot name="edit-dialog-top" />
<AutoForm <AutoForm
v-model="editForm.data" v-model="editForm.data"
v-model:is-valid="editFormValid" v-model:is-valid="editFormValid"
:items="editForm.items" :items="editForm.items"
class="py-2"
/> />
</div> </div>
<template #custom-card-action> <template #custom-card-action>
@@ -153,6 +160,12 @@ defineProps({
type: String, type: String,
required: true, required: true,
}, },
createTitle: {
type: String,
},
editTitle: {
type: String,
},
tableConfig: { tableConfig: {
type: Object as PropType<TableConfig>, type: Object as PropType<TableConfig>,
default: () => ({ default: () => ({

View File

@@ -7,134 +7,137 @@
:width="width" :width="width"
class="my-2" class="my-2"
> >
<v-row> <v-row no-gutters>
<v-col <template v-for="(inputField, index) in items" :key="index">
v-for="(inputField, index) in items" <v-col
:key="index"
cols="12"
sm="12"
>
<v-divider
v-if="inputField.section" v-if="inputField.section"
class="my-2" :cols="12"
/> class="px-2"
<v-card-title
v-if="inputField.section"
class="pl-0"
> >
{{ inputField.section }} <v-divider
</v-card-title> class="my-2"
<v-card-text />
v-if="inputField.sectionDetails" <v-card-title
class="pl-0 mt-0 pt-0" class="pl-0"
>
{{ inputField.section }}
</v-card-title>
<v-card-text
v-if="inputField.sectionDetails"
class="pl-0 mt-0 pt-0"
>
{{ inputField.sectionDetails }}
</v-card-text>
</v-col>
<v-col
:cols="inputField.cols || 12"
class="px-2"
> >
{{ inputField.sectionDetails }} <!-- Check Box -->
</v-card-text> <v-checkbox
v-if="inputField.type === fieldTypes.BOOLEAN"
v-model="model[inputField.varName]"
:name="inputField.varName"
:readonly="fieldState[inputField.varName]?.readonly"
:disabled="fieldState[inputField.varName]?.disabled"
:hint="inputField.hint"
:hide-details="!inputField.hint"
:persistent-hint="!!inputField.hint"
density="comfortable"
validate-on="input"
>
<template #label>
<span class="ml-4">
{{ inputField.label }}
</span>
</template>
</v-checkbox>
<!-- Check Box --> <!-- Text Field -->
<v-checkbox <v-text-field
v-if="inputField.type === fieldTypes.BOOLEAN" v-else-if="inputField.type === fieldTypes.TEXT || inputField.type === fieldTypes.PASSWORD"
v-model="model[inputField.varName]" v-model="model[inputField.varName]"
:name="inputField.varName" :readonly="fieldState[inputField.varName]?.readonly"
:readonly="fieldState[inputField.varName]?.readonly" :disabled="fieldState[inputField.varName]?.disabled"
:disabled="fieldState[inputField.varName]?.disabled" :type="inputField.type === fieldTypes.PASSWORD ? 'password' : 'text'"
:hint="inputField.hint" variant="solo-filled"
:hide-details="!inputField.hint" flat
:persistent-hint="!!inputField.hint" density="comfortable"
density="comfortable" :label="inputField.label"
validate-on="input" :name="inputField.varName"
> :hint="inputField.hint || ''"
<template #label> :rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []"
<span class="ml-4"> validate-on="input"
{{ inputField.label }} />
</span>
</template>
</v-checkbox>
<!-- Text Field --> <!-- Text Area -->
<v-text-field <v-textarea
v-else-if="inputField.type === fieldTypes.TEXT || inputField.type === fieldTypes.PASSWORD" v-else-if="inputField.type === fieldTypes.TEXT_AREA"
v-model="model[inputField.varName]" v-model="model[inputField.varName]"
:readonly="fieldState[inputField.varName]?.readonly" :readonly="fieldState[inputField.varName]?.readonly"
:disabled="fieldState[inputField.varName]?.disabled" :disabled="fieldState[inputField.varName]?.disabled"
:type="inputField.type === fieldTypes.PASSWORD ? 'password' : 'text'" variant="solo-filled"
variant="solo-filled" flat
flat rows="3"
density="comfortable" auto-grow
:label="inputField.label" density="comfortable"
:name="inputField.varName" :label="inputField.label"
:hint="inputField.hint || ''" :name="inputField.varName"
:rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []" :hint="inputField.hint || ''"
validate-on="input" :rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []"
/> validate-on="input"
/>
<!-- Text Area --> <!-- Number Input -->
<v-textarea <v-number-input
v-else-if="inputField.type === fieldTypes.TEXT_AREA" v-else-if="inputField.type === fieldTypes.NUMBER"
v-model="model[inputField.varName]" v-model="model[inputField.varName]"
:readonly="fieldState[inputField.varName]?.readonly" variant="underlined"
:disabled="fieldState[inputField.varName]?.disabled" :control-variant="inputField.numberInputConfig?.controlVariant"
variant="solo-filled" density="comfortable"
flat :label="inputField.label"
rows="3" :name="inputField.varName"
auto-grow :min="inputField.numberInputConfig?.min"
density="comfortable" :max="inputField.numberInputConfig?.max"
:label="inputField.label" :precision="inputField.numberInputConfig?.precision"
:name="inputField.varName" :hint="inputField.hint"
:hint="inputField.hint || ''" :hide-details="!inputField.hint"
:rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []" :persistent-hint="!!inputField.hint"
validate-on="input" :rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []"
/> validate-on="input"
/>
<!-- Number Input --> <!-- Option Select -->
<v-number-input <v-select
v-else-if="inputField.type === fieldTypes.NUMBER" v-else-if="inputField.type === fieldTypes.SELECT"
v-model="model[inputField.varName]" v-model="model[inputField.varName]"
variant="underlined" :readonly="fieldState[inputField.varName]?.readonly"
:control-variant="inputField.numberInputConfig?.controlVariant" :disabled="fieldState[inputField.varName]?.disabled"
density="comfortable" variant="solo-filled"
:label="inputField.label" flat
:name="inputField.varName" :label="inputField.label"
:min="inputField.numberInputConfig?.min" :name="inputField.varName"
:max="inputField.numberInputConfig?.max" :items="inputField.options"
:precision="inputField.numberInputConfig?.precision" item-title="text"
:hint="inputField.hint" :item-value="inputField.selectReturnValue || 'text'"
:hide-details="!inputField.hint" :return-object="false"
:persistent-hint="!!inputField.hint" :hint="inputField.hint"
:rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []" density="comfortable"
validate-on="input" persistent-hint
/> :rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []"
validate-on="input"
/>
<!-- Option Select --> <!-- Color Picker -->
<v-select <div
v-else-if="inputField.type === fieldTypes.SELECT" v-else-if="inputField.type === fieldTypes.COLOR"
v-model="model[inputField.varName]" class="d-flex"
:readonly="fieldState[inputField.varName]?.readonly" style="width: 100%"
:disabled="fieldState[inputField.varName]?.disabled" >
variant="solo-filled" <InputColor v-model="model[inputField.varName]" />
flat </div>
:label="inputField.label" </v-col>
:name="inputField.varName" </template>
:items="inputField.options"
item-title="text"
:item-value="inputField.selectReturnValue || 'text'"
:return-object="false"
:hint="inputField.hint"
density="comfortable"
persistent-hint
:rules="!(inputField.disableUpdate && updateMode) ? inputField.rules || [] : []"
validate-on="input"
/>
<!-- Color Picker -->
<div
v-else-if="inputField.type === fieldTypes.COLOR"
class="d-flex"
style="width: 100%"
>
<InputColor v-model="model[inputField.varName]" />
</div>
</v-col>
</v-row> </v-row>
</v-card> </v-card>
</v-form> </v-form>

View File

@@ -26,8 +26,10 @@ export function useReadOnlyActions<T extends BoundT>(
api: BaseCRUDAPIReadOnly<T>, api: BaseCRUDAPIReadOnly<T>,
allRef: Ref<T[] | null> | null, allRef: Ref<T[] | null> | null,
loading: Ref<boolean>, loading: Ref<boolean>,
defaultQueryParams: Record<string, QueryValue> = {},
): ReadOnlyStoreActions<T> { ): ReadOnlyStoreActions<T> {
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) { function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
params = { ...defaultQueryParams, ...params };
params.orderBy ??= "name"; params.orderBy ??= "name";
params.orderDirection ??= "asc"; params.orderDirection ??= "asc";
@@ -56,6 +58,7 @@ export function useReadOnlyActions<T extends BoundT>(
} }
async function refresh(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) { async function refresh(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
params = { ...defaultQueryParams, ...params };
params.orderBy ??= "name"; params.orderBy ??= "name";
params.orderDirection ??= "asc"; params.orderDirection ??= "asc";
@@ -86,8 +89,10 @@ export function useStoreActions<T extends BoundT>(
api: BaseCRUDAPI<unknown, T, unknown>, api: BaseCRUDAPI<unknown, T, unknown>,
allRef: Ref<T[] | null> | null, allRef: Ref<T[] | null> | null,
loading: Ref<boolean>, loading: Ref<boolean>,
defaultQueryParams: Record<string, QueryValue> = {},
): StoreActions<T> { ): StoreActions<T> {
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) { function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
params = { ...defaultQueryParams, ...params };
params.orderBy ??= "name"; params.orderBy ??= "name";
params.orderDirection ??= "asc"; params.orderDirection ??= "asc";
@@ -116,6 +121,7 @@ export function useStoreActions<T extends BoundT>(
} }
async function refresh(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) { async function refresh(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
params = { ...defaultQueryParams, ...params };
params.orderBy ??= "name"; params.orderBy ??= "name";
params.orderDirection ??= "asc"; params.orderDirection ??= "asc";

View File

@@ -78,7 +78,13 @@ export const useGroupRecipeActions = function (
}; };
const actions = { const actions = {
...useStoreActions<GroupRecipeActionOut>("group-recipe-actions", api.groupRecipeActions, groupRecipeActions, loading), ...useStoreActions<GroupRecipeActionOut>(
"group-recipe-actions",
api.groupRecipeActions,
groupRecipeActions,
loading,
{ orderBy: orderBy },
),
flushStore() { flushStore() {
groupRecipeActions.value = []; groupRecipeActions.value = [];
}, },

View File

@@ -26,6 +26,7 @@ export const useUserForm = () => {
rules: [validators.required], rules: [validators.required],
}, },
{ {
cols: 6,
label: i18n.t("user.password"), label: i18n.t("user.password"),
varName: "password", varName: "password",
disableUpdate: true, disableUpdate: true,
@@ -33,6 +34,7 @@ export const useUserForm = () => {
rules: [validators.required, validators.minLength(8)], rules: [validators.required, validators.minLength(8)],
}, },
{ {
cols: 6,
label: i18n.t("user.authentication-method"), label: i18n.t("user.authentication-method"),
varName: "authMethod", varName: "authMethod",
type: fieldTypes.SELECT, type: fieldTypes.SELECT,
@@ -42,36 +44,42 @@ export const useUserForm = () => {
}, },
{ {
section: i18n.t("user.permissions"), section: i18n.t("user.permissions"),
cols: 6,
label: i18n.t("user.administrator"), label: i18n.t("user.administrator"),
varName: "admin", varName: "admin",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,
rules: [validators.required], rules: [validators.required],
}, },
{ {
cols: 6,
label: i18n.t("user.user-can-invite-other-to-group"), label: i18n.t("user.user-can-invite-other-to-group"),
varName: "canInvite", varName: "canInvite",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,
rules: [validators.required], rules: [validators.required],
}, },
{ {
cols: 6,
label: i18n.t("user.user-can-manage-group"), label: i18n.t("user.user-can-manage-group"),
varName: "canManage", varName: "canManage",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,
rules: [validators.required], rules: [validators.required],
}, },
{ {
cols: 6,
label: i18n.t("user.user-can-organize-group-data"), label: i18n.t("user.user-can-organize-group-data"),
varName: "canOrganize", varName: "canOrganize",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,
rules: [validators.required], rules: [validators.required],
}, },
{ {
cols: 6,
label: i18n.t("user.user-can-manage-household"), label: i18n.t("user.user-can-manage-household"),
varName: "canManageHousehold", varName: "canManageHousehold",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,
rules: [validators.required], rules: [validators.required],
}, },
{ {
cols: 6,
label: i18n.t("user.enable-advanced-features"), label: i18n.t("user.enable-advanced-features"),
varName: "advanced", varName: "advanced",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,

View File

@@ -1086,7 +1086,7 @@
"forgot-password": "Glemt adgangskode", "forgot-password": "Glemt adgangskode",
"forgot-password-text": "Indtast venligst din e-mail-adresse. Vi sender dig en e-mail, så at du kan nulstille din adgangskode.", "forgot-password-text": "Indtast venligst din e-mail-adresse. Vi sender dig en e-mail, så at du kan nulstille din adgangskode.",
"changes-reflected-immediately": "Ændringer til denne bruger vil have effekt med det samme.", "changes-reflected-immediately": "Ændringer til denne bruger vil have effekt med det samme.",
"default-activity": "", "default-activity": "Standardaktivitet",
"default-activity-hint": "Vælg den side, du vil navigere til, når du logger ind fra denne enhed" "default-activity-hint": "Vælg den side, du vil navigere til, når du logger ind fra denne enhed"
}, },
"language-dialog": { "language-dialog": {

View File

@@ -1181,7 +1181,11 @@
"recipe-actions-data": "Recipe Actions Data", "recipe-actions-data": "Recipe Actions Data",
"new-recipe-action": "New Recipe Action", "new-recipe-action": "New Recipe Action",
"edit-recipe-action": "Edit Recipe Action", "edit-recipe-action": "Edit Recipe Action",
"action-type": "Action Type" "action-type": "Action Type",
"action-types": {
"link": "Link",
"post": "Post"
}
}, },
"create-alias": "Create Alias", "create-alias": "Create Alias",
"manage-aliases": "Manage Aliases", "manage-aliases": "Manage Aliases",

View File

@@ -27,92 +27,102 @@
variant="outlined" variant="outlined"
style="border-color: lightgrey;" style="border-color: lightgrey;"
> >
<v-card-text> <v-sheet class="pt-4">
<div class="d-flex"> <v-card-text>
<p> {{ $t("user.user-id-with-value", { id: user.id }) }}</p> <div class="d-flex">
</div> <p> {{ $t("user.user-id-with-value", { id: user.id }) }}</p>
<!-- This is disabled since we can't properly handle changing the user's group in most scenarios --> </div>
<v-select <!-- This is disabled since we can't properly handle changing the user's group in most scenarios -->
v-if="groups"
v-model="user.group"
disabled
:items="groups"
variant="solo-filled"
flat
item-title="name"
item-value="name"
:return-object="false"
:label="$t('group.user-group')"
:rules="[validators.required]"
/>
<v-select
v-if="households"
v-model="user.household"
:items="households"
variant="solo-filled"
flat
item-title="name"
item-value="name"
:return-object="false"
:label="$t('household.user-household')"
:rules="[validators.required]"
/>
<div class="d-flex py-2 pr-2">
<BaseButton
type="button"
:loading="generatingToken"
create
@click.prevent="handlePasswordReset"
>
{{ $t("user.generate-password-reset-link") }}
</BaseButton>
</div>
<div
v-if="resetUrl"
class="mb-2"
>
<v-card-text>
<p class="text-center pb-0">
{{ resetUrl }}
</p>
</v-card-text>
<v-card-actions
class="align-center pt-0"
style="gap: 4px"
>
<BaseButton
cancel
@click="resetUrl = ''"
>
{{ $t("general.close") }}
</BaseButton>
<v-spacer />
<BaseButton
v-if="user.email"
color="info"
class="mr-1"
@click="sendResetEmail"
>
<template #icon>
{{ $globals.icons.email }}
</template>
{{ $t("user.email") }}
</BaseButton>
<AppButtonCopy
:icon="false"
color="info"
:copy-text="resetUrl"
/>
</v-card-actions>
</div>
<AutoForm <v-row>
v-model="user" <v-col cols="6">
:items="userForm" <v-select
update-mode v-if="groups"
:disabled-fields="disabledFields" v-model="user.group"
/> disabled
</v-card-text> :items="groups"
variant="solo-filled"
flat
item-title="name"
item-value="name"
:return-object="false"
:label="$t('group.user-group')"
:rules="[validators.required]"
/>
</v-col>
<v-col cols="6">
<v-select
v-if="households"
v-model="user.household"
:items="households"
variant="solo-filled"
flat
item-title="name"
item-value="name"
:return-object="false"
:label="$t('household.user-household')"
:rules="[validators.required]"
/>
</v-col>
</v-row>
<div class="d-flex py-2 pr-2">
<BaseButton
type="button"
:loading="generatingToken"
create
@click.prevent="handlePasswordReset"
>
{{ $t("user.generate-password-reset-link") }}
</BaseButton>
</div>
<div
v-if="resetUrl"
class="mb-2"
>
<v-card-text>
<p class="text-center pb-0">
{{ resetUrl }}
</p>
</v-card-text>
<v-card-actions
class="align-center pt-0"
style="gap: 4px"
>
<BaseButton
cancel
@click="resetUrl = ''"
>
{{ $t("general.close") }}
</BaseButton>
<v-spacer />
<BaseButton
v-if="user.email"
color="info"
class="mr-1"
@click="sendResetEmail"
>
<template #icon>
{{ $globals.icons.email }}
</template>
{{ $t("user.email") }}
</BaseButton>
<AppButtonCopy
:icon="false"
color="info"
:copy-text="resetUrl"
/>
</v-card-actions>
</div>
<AutoForm
v-model="user"
:items="userForm"
update-mode
:disabled-fields="disabledFields"
/>
</v-card-text>
</v-sheet>
</v-card> </v-card>
<div class="d-flex pa-2"> <div class="d-flex pa-2">
<BaseButton <BaseButton

View File

@@ -20,27 +20,35 @@
> >
<v-card variant="outlined"> <v-card variant="outlined">
<v-card-text> <v-card-text>
<v-select <v-sheet>
v-model="selectedGroup" <v-row>
:items="groups || []" <v-col cols="6">
item-title="name" <v-select
return-object v-model="selectedGroup"
variant="filled" :items="groups || []"
:label="$t('group.user-group')" item-title="name"
:rules="[validators.required]" return-object
/> variant="filled"
<v-select :label="$t('group.user-group')"
v-model="newUserData.household" :rules="[validators.required]"
:disabled="!selectedGroup" />
:items="households" </v-col>
item-title="name" <v-col cols="6">
item-value="name" <v-select
variant="filled" v-model="newUserData.household"
:label="$t('household.user-household')" :disabled="!selectedGroup"
:hint="selectedGroup ? '' : $t('group.you-must-select-a-group-before-selecting-a-household')" :items="households"
persistent-hint item-title="name"
:rules="[validators.required]" item-value="name"
/> variant="filled"
:label="$t('household.user-household')"
:hint="selectedGroup ? '' : $t('group.you-must-select-a-group-before-selecting-a-household')"
persistent-hint
:rules="[validators.required]"
/>
</v-col>
</v-row>
</v-sheet>
<AutoForm <AutoForm
v-model="newUserData" v-model="newUserData"
:items="userForm" :items="userForm"

View File

@@ -3,6 +3,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.categories" :icon="$globals.icons.categories"
:title="$t('data-pages.categories.category-data')" :title="$t('data-pages.categories.category-data')"
:create-title="$t('data-pages.categories.new-category')"
:edit-title="$t('data-pages.categories.edit-category')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="categoryStore.store.value || []" :data="categoryStore.store.value || []"

View File

@@ -130,6 +130,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.foods" :icon="$globals.icons.foods"
:title="$t('data-pages.foods.food-data')" :title="$t('data-pages.foods.food-data')"
:create-title="$t('data-pages.foods.create-food')"
:edit-title="$t('data-pages.foods.edit-food')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="foods || []" :data="foods || []"
@@ -307,6 +309,7 @@ const formItems = computed<AutoFormItems>(() => [
varName: "labelId", varName: "labelId",
type: fieldTypes.SELECT, type: fieldTypes.SELECT,
options: labelOptions.value, options: labelOptions.value,
selectReturnValue: "value",
}, },
{ {
label: i18n.t("tool.on-hand"), label: i18n.t("tool.on-hand"),

View File

@@ -45,6 +45,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.tags" :icon="$globals.icons.tags"
:title="$t('data-pages.labels.labels')" :title="$t('data-pages.labels.labels')"
:create-title="$t('data-pages.labels.new-label')"
:edit-title="$t('data-pages.labels.edit-label')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="labelStore.store.value || []" :data="labelStore.store.value || []"
@@ -66,7 +68,11 @@
</template> </template>
<template #create-dialog-top> <template #create-dialog-top>
<MultiPurposeLabel :label="createForm.data" class="my-2" /> <MultiPurposeLabel v-if="createForm.data.name" :label="createForm.data" class="my-2" />
</template>
<template #edit-dialog-top>
<MultiPurposeLabel v-if="editForm.data.name" :label="editForm.data" class="my-2" />
</template> </template>
<template #table-button-bottom> <template #table-button-bottom>

View File

@@ -3,6 +3,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.categories" :icon="$globals.icons.categories"
:title="$t('data-pages.categories.category-data')" :title="$t('data-pages.categories.category-data')"
:create-title="$t('data-pages.recipe-actions.new-recipe-action')"
:edit-title="$t('data-pages.recipe-actions.edit-recipe-action')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="actionStore.recipeActions.value || []" :data="actionStore.recipeActions.value || []"
@@ -57,11 +59,11 @@ const tableHeaders: TableHeaders[] = [
}, },
]; ];
const actionStore = useGroupRecipeActions(null, null); const actionStore = useGroupRecipeActions();
// ============================================================ // ============================================================
// Form items (shared) // Form items (shared)
const formItems: AutoFormItems = [ const formItems = computed<AutoFormItems>(() => [
{ {
label: i18n.t("general.title"), label: i18n.t("general.title"),
varName: "title", varName: "title",
@@ -78,10 +80,14 @@ const formItems: AutoFormItems = [
label: i18n.t("data-pages.recipe-actions.action-type"), label: i18n.t("data-pages.recipe-actions.action-type"),
varName: "actionType", varName: "actionType",
type: fieldTypes.SELECT, type: fieldTypes.SELECT,
options: [{ text: "link" }, { text: "post" }], options: [
{ text: i18n.t("data-pages.recipe-actions.action-types.link"), value: "link" },
{ text: i18n.t("data-pages.recipe-actions.action-types.post"), value: "post" },
],
selectReturnValue: "value",
rules: [validators.required], rules: [validators.required],
}, },
]; ]);
// ============================================================ // ============================================================
// Create // Create

View File

@@ -3,6 +3,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.tags" :icon="$globals.icons.tags"
:title="$t('data-pages.tags.tag-data')" :title="$t('data-pages.tags.tag-data')"
:create-title="$t('data-pages.tags.new-tag')"
:edit-title="$t('data-pages.tags.edit-tag')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="tagStore.store.value || []" :data="tagStore.store.value || []"

View File

@@ -3,6 +3,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.tools" :icon="$globals.icons.tools"
:title="$t('data-pages.tools.tool-data')" :title="$t('data-pages.tools.tool-data')"
:create-title="$t('data-pages.tools.new-tool')"
:edit-title="$t('data-pages.tools.edit-tool')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="tools || []" :data="tools || []"

View File

@@ -95,6 +95,8 @@
<GroupDataPage <GroupDataPage
:icon="$globals.icons.units" :icon="$globals.icons.units"
:title="$t('general.units')" :title="$t('general.units')"
:create-title="$t('data-pages.units.create-unit')"
:edit-title="$t('data-pages.units.edit-unit')"
:table-headers="tableHeaders" :table-headers="tableHeaders"
:table-config="tableConfig" :table-config="tableConfig"
:data="unitStore || []" :data="unitStore || []"
@@ -247,22 +249,26 @@ type StandardizedUnitTypeOption = {
const formItems = computed<AutoFormItems>(() => [ const formItems = computed<AutoFormItems>(() => [
{ {
cols: 8,
label: i18n.t("general.name"), label: i18n.t("general.name"),
varName: "name", varName: "name",
type: fieldTypes.TEXT, type: fieldTypes.TEXT,
rules: [validators.required], rules: [validators.required],
}, },
{ {
label: i18n.t("general.plural-name"), cols: 4,
varName: "pluralName",
type: fieldTypes.TEXT,
},
{
label: i18n.t("data-pages.units.abbreviation"), label: i18n.t("data-pages.units.abbreviation"),
varName: "abbreviation", varName: "abbreviation",
type: fieldTypes.TEXT, type: fieldTypes.TEXT,
}, },
{ {
cols: 8,
label: i18n.t("general.plural-name"),
varName: "pluralName",
type: fieldTypes.TEXT,
},
{
cols: 4,
label: i18n.t("data-pages.units.plural-abbreviation"), label: i18n.t("data-pages.units.plural-abbreviation"),
varName: "pluralAbbreviation", varName: "pluralAbbreviation",
type: fieldTypes.TEXT, type: fieldTypes.TEXT,
@@ -273,11 +279,14 @@ const formItems = computed<AutoFormItems>(() => [
type: fieldTypes.TEXT, type: fieldTypes.TEXT,
}, },
{ {
section: i18n.t("general.settings"),
cols: 4,
label: i18n.t("data-pages.units.use-abbv"), label: i18n.t("data-pages.units.use-abbv"),
varName: "useAbbreviation", varName: "useAbbreviation",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,
}, },
{ {
cols: 4,
label: i18n.t("data-pages.units.fraction"), label: i18n.t("data-pages.units.fraction"),
varName: "fraction", varName: "fraction",
type: fieldTypes.BOOLEAN, type: fieldTypes.BOOLEAN,

View File

@@ -28,6 +28,7 @@ export interface FormFieldNumberInputConfig {
export interface FormField { export interface FormField {
section?: string; section?: string;
sectionDetails?: string; sectionDetails?: string;
cols?: number | "auto";
label?: string; label?: string;
hint?: string; hint?: string;
varName: string; varName: string;

File diff suppressed because it is too large Load Diff

View File

@@ -198,7 +198,7 @@
"romaine lettuce": { "romaine lettuce": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "romaine sla", "name": "Romeinse sla",
"plural_name": "romaine sla" "plural_name": "romaine sla"
}, },
"beetroot": { "beetroot": {
@@ -2577,8 +2577,8 @@
"salad cheese": { "salad cheese": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "salad cheese", "name": "salade kaas",
"plural_name": "salad cheese" "plural_name": "Salade kaas"
}, },
"truffle cheese": { "truffle cheese": {
"aliases": [], "aliases": [],
@@ -3559,8 +3559,8 @@
"coconut manna": { "coconut manna": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "coconut manna", "name": "kokosnoot manna",
"plural_name": "coconut mannas" "plural_name": "kokosnoten manna"
}, },
"falafel mix": { "falafel mix": {
"aliases": [], "aliases": [],
@@ -3661,8 +3661,8 @@
"egg tofu": { "egg tofu": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "egg tofu", "name": "ei tofu",
"plural_name": "egg tofus" "plural_name": "ei tofu"
}, },
"protein drink": { "protein drink": {
"aliases": [], "aliases": [],
@@ -3917,7 +3917,7 @@
"kielbasa": { "kielbasa": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "kielbasa", "name": "kielbasa worst",
"plural_name": "kielbasas" "plural_name": "kielbasas"
}, },
"pork belly": { "pork belly": {
@@ -4313,14 +4313,14 @@
"sausage patty": { "sausage patty": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "sausage patty", "name": "worstenburger",
"plural_name": "sausage patties" "plural_name": "worst burgers"
}, },
"beef suet": { "beef suet": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "beef suet", "name": "rundervet",
"plural_name": "beef suets" "plural_name": "rundervetten"
}, },
"veal roast": { "veal roast": {
"aliases": [], "aliases": [],
@@ -4467,8 +4467,8 @@
"cornish hen": { "cornish hen": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "cornish hen", "name": "piepkuiken",
"plural_name": "cornish hens" "plural_name": "piepkuikens"
}, },
"deli turkey": { "deli turkey": {
"aliases": [], "aliases": [],
@@ -4851,26 +4851,26 @@
"duck bacon": { "duck bacon": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "duck bacon", "name": "eenden spek",
"plural_name": "duck bacons" "plural_name": "eenden spekken"
}, },
"pulled turkey": { "pulled turkey": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "pulled turkey", "name": "getrokken kalkoen",
"plural_name": "pulled turkeys" "plural_name": "getrokken kalkoenen"
}, },
"chicken gyro": { "chicken gyro": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chicken gyro", "name": "kip gyro",
"plural_name": "chicken gyros" "plural_name": "kip gyros"
}, },
"chicken patty": { "chicken patty": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chicken patty", "name": "kip burger",
"plural_name": "chicken patties" "plural_name": "kip burgers"
}, },
"chicken rib": { "chicken rib": {
"aliases": [], "aliases": [],