mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-27 02:03:13 -05:00
feat: Improve auto-form layout (#7150)
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
<!-- Create Dialog -->
|
||||
<BaseDialog
|
||||
v-model="createDialog"
|
||||
:title="$t('general.create')"
|
||||
:title="createTitle || $t('general.create')"
|
||||
:icon="icon"
|
||||
color="primary"
|
||||
max-width="600px"
|
||||
width="100%"
|
||||
:submit-disabled="!createFormValid"
|
||||
can-confirm
|
||||
@confirm="emit('create-one', createForm.data)"
|
||||
@@ -15,6 +17,7 @@
|
||||
v-model="createForm.data"
|
||||
v-model:is-valid="createFormValid"
|
||||
:items="createForm.items"
|
||||
class="py-2"
|
||||
/>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
@@ -22,18 +25,22 @@
|
||||
<!-- Edit Dialog -->
|
||||
<BaseDialog
|
||||
v-model="editDialog"
|
||||
:title="$t('general.edit')"
|
||||
:title="editTitle || $t('general.edit')"
|
||||
:icon="icon"
|
||||
color="primary"
|
||||
max-width="600px"
|
||||
width="100%"
|
||||
:submit-disabled="!editFormValid"
|
||||
can-confirm
|
||||
@confirm="emit('edit-one', editForm.data)"
|
||||
>
|
||||
<div class="mx-2 mt-2">
|
||||
<slot name="edit-dialog-top" />
|
||||
<AutoForm
|
||||
v-model="editForm.data"
|
||||
v-model:is-valid="editFormValid"
|
||||
:items="editForm.items"
|
||||
class="py-2"
|
||||
/>
|
||||
</div>
|
||||
<template #custom-card-action>
|
||||
@@ -153,6 +160,12 @@ defineProps({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createTitle: {
|
||||
type: String,
|
||||
},
|
||||
editTitle: {
|
||||
type: String,
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object as PropType<TableConfig>,
|
||||
default: () => ({
|
||||
|
||||
@@ -7,19 +7,17 @@
|
||||
:width="width"
|
||||
class="my-2"
|
||||
>
|
||||
<v-row>
|
||||
<v-row no-gutters>
|
||||
<template v-for="(inputField, index) in items" :key="index">
|
||||
<v-col
|
||||
v-for="(inputField, index) in items"
|
||||
:key="index"
|
||||
cols="12"
|
||||
sm="12"
|
||||
v-if="inputField.section"
|
||||
:cols="12"
|
||||
class="px-2"
|
||||
>
|
||||
<v-divider
|
||||
v-if="inputField.section"
|
||||
class="my-2"
|
||||
/>
|
||||
<v-card-title
|
||||
v-if="inputField.section"
|
||||
class="pl-0"
|
||||
>
|
||||
{{ inputField.section }}
|
||||
@@ -30,7 +28,11 @@
|
||||
>
|
||||
{{ inputField.sectionDetails }}
|
||||
</v-card-text>
|
||||
|
||||
</v-col>
|
||||
<v-col
|
||||
:cols="inputField.cols || 12"
|
||||
class="px-2"
|
||||
>
|
||||
<!-- Check Box -->
|
||||
<v-checkbox
|
||||
v-if="inputField.type === fieldTypes.BOOLEAN"
|
||||
@@ -116,6 +118,7 @@
|
||||
<InputColor v-model="model[inputField.varName]" />
|
||||
</div>
|
||||
</v-col>
|
||||
</template>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</v-form>
|
||||
|
||||
@@ -26,8 +26,10 @@ export function useReadOnlyActions<T extends BoundT>(
|
||||
api: BaseCRUDAPIReadOnly<T>,
|
||||
allRef: Ref<T[] | null> | null,
|
||||
loading: Ref<boolean>,
|
||||
defaultQueryParams: Record<string, QueryValue> = {},
|
||||
): ReadOnlyStoreActions<T> {
|
||||
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
params = { ...defaultQueryParams, ...params };
|
||||
params.orderBy ??= "name";
|
||||
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>) {
|
||||
params = { ...defaultQueryParams, ...params };
|
||||
params.orderBy ??= "name";
|
||||
params.orderDirection ??= "asc";
|
||||
|
||||
@@ -86,8 +89,10 @@ export function useStoreActions<T extends BoundT>(
|
||||
api: BaseCRUDAPI<unknown, T, unknown>,
|
||||
allRef: Ref<T[] | null> | null,
|
||||
loading: Ref<boolean>,
|
||||
defaultQueryParams: Record<string, QueryValue> = {},
|
||||
): StoreActions<T> {
|
||||
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
params = { ...defaultQueryParams, ...params };
|
||||
params.orderBy ??= "name";
|
||||
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>) {
|
||||
params = { ...defaultQueryParams, ...params };
|
||||
params.orderBy ??= "name";
|
||||
params.orderDirection ??= "asc";
|
||||
|
||||
|
||||
@@ -78,7 +78,13 @@ export const useGroupRecipeActions = function (
|
||||
};
|
||||
|
||||
const actions = {
|
||||
...useStoreActions<GroupRecipeActionOut>("group-recipe-actions", api.groupRecipeActions, groupRecipeActions, loading),
|
||||
...useStoreActions<GroupRecipeActionOut>(
|
||||
"group-recipe-actions",
|
||||
api.groupRecipeActions,
|
||||
groupRecipeActions,
|
||||
loading,
|
||||
{ orderBy: orderBy },
|
||||
),
|
||||
flushStore() {
|
||||
groupRecipeActions.value = [];
|
||||
},
|
||||
|
||||
@@ -26,6 +26,7 @@ export const useUserForm = () => {
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.password"),
|
||||
varName: "password",
|
||||
disableUpdate: true,
|
||||
@@ -33,6 +34,7 @@ export const useUserForm = () => {
|
||||
rules: [validators.required, validators.minLength(8)],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.authentication-method"),
|
||||
varName: "authMethod",
|
||||
type: fieldTypes.SELECT,
|
||||
@@ -42,36 +44,42 @@ export const useUserForm = () => {
|
||||
},
|
||||
{
|
||||
section: i18n.t("user.permissions"),
|
||||
cols: 6,
|
||||
label: i18n.t("user.administrator"),
|
||||
varName: "admin",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.user-can-invite-other-to-group"),
|
||||
varName: "canInvite",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.user-can-manage-group"),
|
||||
varName: "canManage",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.user-can-organize-group-data"),
|
||||
varName: "canOrganize",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.user-can-manage-household"),
|
||||
varName: "canManageHousehold",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
cols: 6,
|
||||
label: i18n.t("user.enable-advanced-features"),
|
||||
varName: "advanced",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
|
||||
@@ -1168,7 +1168,11 @@
|
||||
"recipe-actions-data": "Recipe Actions Data",
|
||||
"new-recipe-action": "New 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",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
|
||||
@@ -27,11 +27,15 @@
|
||||
variant="outlined"
|
||||
style="border-color: lightgrey;"
|
||||
>
|
||||
<v-sheet class="pt-4">
|
||||
<v-card-text>
|
||||
<div class="d-flex">
|
||||
<p> {{ $t("user.user-id-with-value", { id: user.id }) }}</p>
|
||||
</div>
|
||||
<!-- This is disabled since we can't properly handle changing the user's group in most scenarios -->
|
||||
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-select
|
||||
v-if="groups"
|
||||
v-model="user.group"
|
||||
@@ -45,6 +49,8 @@
|
||||
:label="$t('group.user-group')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-select
|
||||
v-if="households"
|
||||
v-model="user.household"
|
||||
@@ -57,6 +63,8 @@
|
||||
:label="$t('household.user-household')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<div class="d-flex py-2 pr-2">
|
||||
<BaseButton
|
||||
type="button"
|
||||
@@ -67,6 +75,7 @@
|
||||
{{ $t("user.generate-password-reset-link") }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="resetUrl"
|
||||
class="mb-2"
|
||||
@@ -113,6 +122,7 @@
|
||||
:disabled-fields="disabledFields"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-sheet>
|
||||
</v-card>
|
||||
<div class="d-flex pa-2">
|
||||
<BaseButton
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
>
|
||||
<v-card variant="outlined">
|
||||
<v-card-text>
|
||||
<v-sheet>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-select
|
||||
v-model="selectedGroup"
|
||||
:items="groups || []"
|
||||
@@ -29,6 +32,8 @@
|
||||
:label="$t('group.user-group')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-select
|
||||
v-model="newUserData.household"
|
||||
:disabled="!selectedGroup"
|
||||
@@ -41,6 +46,9 @@
|
||||
persistent-hint
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-sheet>
|
||||
<AutoForm
|
||||
v-model="newUserData"
|
||||
:items="userForm"
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.categories"
|
||||
: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-config="tableConfig"
|
||||
:data="categoryStore.store.value || []"
|
||||
|
||||
@@ -130,6 +130,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.foods"
|
||||
: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-config="tableConfig"
|
||||
:data="foods || []"
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.tags"
|
||||
: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-config="tableConfig"
|
||||
:data="labelStore.store.value || []"
|
||||
@@ -66,7 +68,11 @@
|
||||
</template>
|
||||
|
||||
<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 #table-button-bottom>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.categories"
|
||||
: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-config="tableConfig"
|
||||
:data="actionStore.recipeActions.value || []"
|
||||
@@ -57,11 +59,11 @@ const tableHeaders: TableHeaders[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const actionStore = useGroupRecipeActions(null, null);
|
||||
const actionStore = useGroupRecipeActions();
|
||||
|
||||
// ============================================================
|
||||
// Form items (shared)
|
||||
const formItems: AutoFormItems = [
|
||||
const formItems = computed<AutoFormItems>(() => [
|
||||
{
|
||||
label: i18n.t("general.title"),
|
||||
varName: "title",
|
||||
@@ -78,10 +80,14 @@ const formItems: AutoFormItems = [
|
||||
label: i18n.t("data-pages.recipe-actions.action-type"),
|
||||
varName: "actionType",
|
||||
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],
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
// ============================================================
|
||||
// Create
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.tags"
|
||||
: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-config="tableConfig"
|
||||
:data="tagStore.store.value || []"
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.tools"
|
||||
: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-config="tableConfig"
|
||||
:data="tools || []"
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
<GroupDataPage
|
||||
:icon="$globals.icons.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-config="tableConfig"
|
||||
:data="unitStore || []"
|
||||
@@ -231,22 +233,26 @@ const { store: unitStore, actions: unitActions } = useUnitStore();
|
||||
// Form items (shared)
|
||||
const formItems: AutoFormItems = [
|
||||
{
|
||||
cols: 8,
|
||||
label: i18n.t("general.name"),
|
||||
varName: "name",
|
||||
type: fieldTypes.TEXT,
|
||||
rules: [validators.required],
|
||||
},
|
||||
{
|
||||
label: i18n.t("general.plural-name"),
|
||||
varName: "pluralName",
|
||||
type: fieldTypes.TEXT,
|
||||
},
|
||||
{
|
||||
cols: 4,
|
||||
label: i18n.t("data-pages.units.abbreviation"),
|
||||
varName: "abbreviation",
|
||||
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"),
|
||||
varName: "pluralAbbreviation",
|
||||
type: fieldTypes.TEXT,
|
||||
@@ -257,11 +263,14 @@ const formItems: AutoFormItems = [
|
||||
type: fieldTypes.TEXT,
|
||||
},
|
||||
{
|
||||
section: i18n.t("general.settings"),
|
||||
cols: 4,
|
||||
label: i18n.t("data-pages.units.use-abbv"),
|
||||
varName: "useAbbreviation",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
},
|
||||
{
|
||||
cols: 4,
|
||||
label: i18n.t("data-pages.units.fraction"),
|
||||
varName: "fraction",
|
||||
type: fieldTypes.BOOLEAN,
|
||||
|
||||
@@ -6,11 +6,13 @@ export type FormValidationRule = (value: any) => boolean | string;
|
||||
|
||||
export interface FormSelectOption {
|
||||
text: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface FormField {
|
||||
section?: string;
|
||||
sectionDetails?: string;
|
||||
cols?: number | "auto";
|
||||
label?: string;
|
||||
hint?: string;
|
||||
varName: string;
|
||||
@@ -19,7 +21,7 @@ export interface FormField {
|
||||
disableUpdate?: boolean;
|
||||
disableCreate?: boolean;
|
||||
options?: FormSelectOption[];
|
||||
selectReturnValue?: string;
|
||||
selectReturnValue?: "text" | "value";
|
||||
}
|
||||
|
||||
export type AutoFormItems = FormField[];
|
||||
|
||||
Reference in New Issue
Block a user