mirror of
https://github.com/frappe/books.git
synced 2024-12-22 10:58:59 +00:00
refactor(ui): use showDialog for FB Dialog Component
- remove showMessageDialog using native component - add icons to the Dialog component
This commit is contained in:
parent
524111418c
commit
080eaa5e4f
@ -4,8 +4,8 @@ import { DateTime } from 'luxon';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { codeStateMap } from 'regional/in';
|
||||
import { ExportExtention } from 'reports/types';
|
||||
import { showDialog } from 'src/utils/interactive';
|
||||
import { getSavePath } from 'src/utils/ipcCalls';
|
||||
import { showMessageDialog } from 'src/utils/ui';
|
||||
import { invertMap } from 'utils';
|
||||
import { getCsvData, saveExportData } from '../commonExporter';
|
||||
import { BaseGSTR } from './BaseGSTR';
|
||||
@ -175,9 +175,10 @@ async function getCanExport(report: BaseGSTR) {
|
||||
return true;
|
||||
}
|
||||
|
||||
showMessageDialog({
|
||||
message: 'Cannot Export',
|
||||
detail: 'Please set GSTIN in General Settings.',
|
||||
showDialog({
|
||||
title: report.fyo.t`Cannot Export`,
|
||||
detail: report.fyo.t`Please set GSTIN in General Settings.`,
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
return false;
|
||||
|
@ -18,9 +18,16 @@
|
||||
inner
|
||||
"
|
||||
>
|
||||
<h1 class="font-semibold">{{ title }}</h1>
|
||||
<p v-if="description" class="text-base">{{ description }}</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="font-semibold">{{ title }}</h1>
|
||||
<FeatherIcon
|
||||
:name="config.iconName"
|
||||
class="w-6 h-6"
|
||||
:class="config.iconColor"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="detail" class="text-base">{{ detail }}</p>
|
||||
<div class="flex justify-end gap-4 mt-4">
|
||||
<Button
|
||||
v-for="(b, index) of buttons"
|
||||
:ref="b.isPrimary ? 'primary' : 'secondary'"
|
||||
@ -38,9 +45,11 @@
|
||||
</Teleport>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { DialogButton } from 'src/utils/types';
|
||||
import { getIconConfig } from 'src/utils/interactive';
|
||||
import { DialogButton, ToastType } from 'src/utils/types';
|
||||
import { defineComponent, nextTick, PropType, ref } from 'vue';
|
||||
import Button from './Button.vue';
|
||||
import FeatherIcon from './FeatherIcon.vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
@ -53,8 +62,9 @@ export default defineComponent({
|
||||
return { open: false };
|
||||
},
|
||||
props: {
|
||||
type: { type: String as PropType<ToastType>, default: 'info' },
|
||||
title: { type: String, required: true },
|
||||
description: {
|
||||
detail: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
@ -79,6 +89,11 @@ export default defineComponent({
|
||||
|
||||
this.focusButton();
|
||||
},
|
||||
computed: {
|
||||
config() {
|
||||
return getIconConfig(this.type);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
focusButton() {
|
||||
let button = this.primary?.[0];
|
||||
@ -104,10 +119,7 @@ export default defineComponent({
|
||||
return this.handleClick(0);
|
||||
}
|
||||
|
||||
const index = this.buttons.findIndex(
|
||||
({ isPrimary, isEscape }) =>
|
||||
isEscape || (this.buttons.length === 2 && !isPrimary)
|
||||
);
|
||||
const index = this.buttons.findIndex(({ isEscape }) => isEscape);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
@ -117,11 +129,11 @@ export default defineComponent({
|
||||
},
|
||||
handleClick(index: number) {
|
||||
const button = this.buttons[index];
|
||||
button.handler();
|
||||
button.action();
|
||||
this.open = false;
|
||||
},
|
||||
},
|
||||
components: { Button },
|
||||
components: { Button, FeatherIcon },
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
|
@ -20,9 +20,9 @@
|
||||
style="pointer-events: auto"
|
||||
>
|
||||
<feather-icon
|
||||
:name="iconName"
|
||||
:name="config.iconName"
|
||||
class="w-6 h-6 me-3"
|
||||
:class="iconColor"
|
||||
:class="config.iconColor"
|
||||
/>
|
||||
<div @click="actionClicked" :class="actionText ? 'cursor-pointer' : ''">
|
||||
<p class="text-base">{{ message }}</p>
|
||||
@ -51,6 +51,7 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { getColorClass } from 'src/utils/colors';
|
||||
import { getIconConfig } from 'src/utils/interactive';
|
||||
import { ToastDuration, ToastType } from 'src/utils/types';
|
||||
import { toastDurationMap } from 'src/utils/ui';
|
||||
import { defineComponent, nextTick, PropType } from 'vue';
|
||||
@ -73,26 +74,8 @@ export default defineComponent({
|
||||
duration: { type: String as PropType<ToastDuration>, default: 'long' },
|
||||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
switch (this.type) {
|
||||
case 'warning':
|
||||
return 'alert-triangle';
|
||||
case 'success':
|
||||
return 'check-circle';
|
||||
default:
|
||||
return 'alert-circle';
|
||||
}
|
||||
},
|
||||
color() {
|
||||
return {
|
||||
info: 'blue',
|
||||
warning: 'orange',
|
||||
error: 'red',
|
||||
success: 'green',
|
||||
}[this.type];
|
||||
},
|
||||
iconColor() {
|
||||
return getColorClass(this.color ?? 'gray', 'text', 400);
|
||||
config() {
|
||||
return getIconConfig(this.type);
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
|
@ -5,12 +5,12 @@ import { Doc } from 'fyo/model/doc';
|
||||
import { BaseError } from 'fyo/utils/errors';
|
||||
import { ErrorLog } from 'fyo/utils/types';
|
||||
import { truncate } from 'lodash';
|
||||
import { showDialog } from 'src/utils/interactive';
|
||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||
import { fyo } from './initFyo';
|
||||
import router from './router';
|
||||
import { getErrorMessage, stringifyCircular } from './utils';
|
||||
import { MessageDialogOptions, ToastOptions } from './utils/types';
|
||||
import { showMessageDialog } from './utils/ui';
|
||||
import { DialogOptions, ToastOptions } from './utils/types';
|
||||
|
||||
function shouldNotStore(error: Error) {
|
||||
const shouldLog = (error as BaseError).shouldStore ?? true;
|
||||
@ -108,9 +108,10 @@ export async function handleErrorWithDialog(
|
||||
await handleError(false, error, { errorMessage, doc });
|
||||
|
||||
const label = getErrorLabel(error);
|
||||
const options: MessageDialogOptions = {
|
||||
message: label,
|
||||
const options: DialogOptions = {
|
||||
title: label,
|
||||
detail: errorMessage,
|
||||
type: 'error',
|
||||
};
|
||||
|
||||
if (reportError) {
|
||||
@ -121,12 +122,13 @@ export async function handleErrorWithDialog(
|
||||
action() {
|
||||
reportIssue(getErrorLogObject(error, { errorMessage }));
|
||||
},
|
||||
isPrimary: true,
|
||||
},
|
||||
{ label: t`Cancel`, action() {} },
|
||||
{ label: t`Cancel`, action() {}, isEscape: true },
|
||||
];
|
||||
}
|
||||
|
||||
await showMessageDialog(options);
|
||||
await showDialog(options);
|
||||
if (dontThrow) {
|
||||
if (fyo.store.isDevelopment) {
|
||||
console.error(error);
|
||||
|
@ -223,9 +223,9 @@ import FeatherIcon from 'src/components/FeatherIcon.vue';
|
||||
import Loading from 'src/components/Loading.vue';
|
||||
import Modal from 'src/components/Modal.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { showDialog } from 'src/utils/interactive';
|
||||
import { deleteDb, getSavePath } from 'src/utils/ipcCalls';
|
||||
import { updateConfigFiles } from 'src/utils/misc';
|
||||
import { showMessageDialog } from 'src/utils/ui';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
|
||||
export default {
|
||||
@ -264,9 +264,10 @@ export default {
|
||||
const file = this.files[i];
|
||||
const vm = this;
|
||||
|
||||
await showMessageDialog({
|
||||
message: t`Delete ${file.companyName}?`,
|
||||
await showDialog({
|
||||
title: t`Delete ${file.companyName}?`,
|
||||
detail: t`Database file: ${file.dbPath}`,
|
||||
type: 'warning',
|
||||
buttons: [
|
||||
{
|
||||
label: this.t`Yes`,
|
||||
@ -274,10 +275,12 @@ export default {
|
||||
await deleteDb(file.dbPath);
|
||||
await vm.setFiles();
|
||||
},
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
label: this.t`No`,
|
||||
action() {},
|
||||
isEscape: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -383,10 +383,11 @@ import Modal from 'src/components/Modal.vue';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { getColumnLabel, Importer, TemplateField } from 'src/importer';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { showDialog } from 'src/utils/interactive';
|
||||
import { getSavePath, saveData, selectFile } from 'src/utils/ipcCalls';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { selectTextFile, showMessageDialog } from 'src/utils/ui';
|
||||
import { selectTextFile } from 'src/utils/ui';
|
||||
import { defineComponent } from 'vue';
|
||||
import Loading from '../components/Loading.vue';
|
||||
|
||||
@ -780,10 +781,11 @@ export default defineComponent({
|
||||
await saveData(template, filePath);
|
||||
},
|
||||
async preImportValidations(): Promise<boolean> {
|
||||
const message = this.t`Cannot Import`;
|
||||
const title = this.t`Cannot Import`;
|
||||
if (this.errorMessage.length) {
|
||||
await showMessageDialog({
|
||||
message,
|
||||
await showDialog({
|
||||
title,
|
||||
type: 'error',
|
||||
detail: this.errorMessage,
|
||||
});
|
||||
return false;
|
||||
@ -791,8 +793,9 @@ export default defineComponent({
|
||||
|
||||
const cellErrors = this.importer.checkCellErrors();
|
||||
if (cellErrors.length) {
|
||||
await showMessageDialog({
|
||||
message,
|
||||
await showDialog({
|
||||
title,
|
||||
type: 'error',
|
||||
detail: this.t`Following cells have errors: ${cellErrors.join(', ')}`,
|
||||
});
|
||||
return false;
|
||||
@ -800,8 +803,9 @@ export default defineComponent({
|
||||
|
||||
const absentLinks = await this.importer.checkLinks();
|
||||
if (absentLinks.length) {
|
||||
await showMessageDialog({
|
||||
message,
|
||||
await showDialog({
|
||||
title,
|
||||
type: 'error',
|
||||
detail: this.t`Following links do not exist: ${absentLinks
|
||||
.map((l) => `(${l.schemaLabel}, ${l.name})`)
|
||||
.join(', ')}`,
|
||||
@ -851,18 +855,22 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
let shouldSubmit = false;
|
||||
await showMessageDialog({
|
||||
message: this.t`Should entries be submitted after syncing?`,
|
||||
await showDialog({
|
||||
title: this.t`Submit entries?`,
|
||||
type: 'info',
|
||||
details: this.t`Should entries be submitted after syncing?`,
|
||||
buttons: [
|
||||
{
|
||||
label: this.t`Yes`,
|
||||
action() {
|
||||
shouldSubmit = true;
|
||||
},
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
label: this.t`No`,
|
||||
action() {},
|
||||
isEscape: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -916,9 +924,10 @@ export default defineComponent({
|
||||
|
||||
const isValid = this.importer.selectFile(text);
|
||||
if (!isValid) {
|
||||
await showMessageDialog({
|
||||
message: this.t`Bad import data`,
|
||||
detail: this.t`Could not read file`,
|
||||
await showDialog({
|
||||
title: this.t`Cannot read file`,
|
||||
detail: this.t`Bad import data, could not read file`,
|
||||
type: 'error',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -76,11 +76,9 @@ import Button from 'src/components/Button.vue';
|
||||
import FormContainer from 'src/components/FormContainer.vue';
|
||||
import FormHeader from 'src/components/FormHeader.vue';
|
||||
import { getErrorMessage } from 'src/utils';
|
||||
import { showDialog } from 'src/utils/interactive';
|
||||
import { getSetupWizardDoc } from 'src/utils/misc';
|
||||
import {
|
||||
getFieldsGroupedByTabAndSection,
|
||||
showMessageDialog,
|
||||
} from 'src/utils/ui';
|
||||
import { getFieldsGroupedByTabAndSection } from 'src/utils/ui';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import CommonFormSection from '../CommonForm/CommonFormSection.vue';
|
||||
|
||||
@ -155,8 +153,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
if (!this.areAllValuesFilled) {
|
||||
return await showMessageDialog({
|
||||
message: this.t`Please fill all values`,
|
||||
return await showDialog({
|
||||
title: this.t`Mandatory Error`,
|
||||
detail: this.t`Please fill all values`,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ import PageHeader from 'src/components/PageHeader.vue';
|
||||
import ShortcutKeys from 'src/components/ShortcutKeys.vue';
|
||||
import { handleErrorWithDialog } from 'src/errorHandling';
|
||||
import { shortcutsKey } from 'src/utils/injectionKeys';
|
||||
import { showToast } from 'src/utils/interactive';
|
||||
import { showDialog, showToast } from 'src/utils/interactive';
|
||||
import { getSavePath } from 'src/utils/ipcCalls';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import {
|
||||
@ -237,7 +237,6 @@ import {
|
||||
openSettings,
|
||||
selectTextFile,
|
||||
ShortcutKey,
|
||||
showMessageDialog,
|
||||
} from 'src/utils/ui';
|
||||
import { useDocShortcuts } from 'src/utils/vueUtils';
|
||||
import { getMapFromList } from 'utils/index';
|
||||
@ -460,10 +459,11 @@ export default defineComponent({
|
||||
const name = names[0]?.name;
|
||||
if (!name) {
|
||||
const label = this.fyo.schemaMap[schemaName]?.label ?? schemaName;
|
||||
await showMessageDialog({
|
||||
message: this.t`No Display Entries Found`,
|
||||
await showDialog({
|
||||
title: this.t`No Display Entries Found`,
|
||||
detail: this
|
||||
.t`Please create a ${label} entry to view Template Preview`,
|
||||
type: 'warning',
|
||||
});
|
||||
|
||||
return;
|
||||
|
@ -1,36 +1,38 @@
|
||||
import { t } from 'fyo';
|
||||
import Dialog from 'src/components/Dialog.vue';
|
||||
import Toast from 'src/components/Toast.vue';
|
||||
import { t } from 'fyo';
|
||||
import { App, createApp, h } from 'vue';
|
||||
import { DialogButton, DialogOptions, ToastOptions } from './types';
|
||||
import { getColorClass } from './colors';
|
||||
import { DialogButton, DialogOptions, ToastOptions, ToastType } from './types';
|
||||
|
||||
type DialogReturn<DO extends DialogOptions> = DO['buttons'] extends {
|
||||
handler: () => Promise<infer O> | infer O;
|
||||
action: () => Promise<infer O> | infer O;
|
||||
}[]
|
||||
? O
|
||||
: void;
|
||||
|
||||
export async function showDialog<DO extends DialogOptions>(options: DO) {
|
||||
const { title, description } = options;
|
||||
|
||||
const preWrappedButtons: DialogButton[] = options.buttons ?? [
|
||||
{ label: t`Okay`, handler: () => {} },
|
||||
{ label: t`Okay`, action: () => {}, isEscape: true },
|
||||
];
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
const buttons = preWrappedButtons!.map(({ label, handler, isPrimary }) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const buttons = preWrappedButtons!.map((config) => {
|
||||
return {
|
||||
label,
|
||||
handler: async () => {
|
||||
resolve(await handler());
|
||||
...config,
|
||||
action: async () => {
|
||||
try {
|
||||
resolve(await config.action());
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
isPrimary,
|
||||
};
|
||||
});
|
||||
|
||||
const dialogApp = createApp({
|
||||
render() {
|
||||
return h(Dialog, { title, description, buttons });
|
||||
return h(Dialog, { ...options, buttons });
|
||||
},
|
||||
});
|
||||
|
||||
@ -55,3 +57,23 @@ function fragmentMountComponent(app: App<Element>) {
|
||||
app.mount(fragment);
|
||||
document.body.append(fragment);
|
||||
}
|
||||
|
||||
export function getIconConfig(type: ToastType) {
|
||||
let iconName = 'alert-circle';
|
||||
if (type === 'warning') {
|
||||
iconName = 'alert-triangle';
|
||||
} else if (type === 'success') {
|
||||
iconName = 'check-circle';
|
||||
}
|
||||
|
||||
const color = {
|
||||
info: 'blue',
|
||||
warning: 'orange',
|
||||
error: 'red',
|
||||
success: 'green',
|
||||
}[type];
|
||||
|
||||
const iconColor = getColorClass(color ?? 'gray', 'text', 400);
|
||||
|
||||
return { iconName, color, iconColor };
|
||||
}
|
||||
|
@ -7,9 +7,8 @@ import { BaseError } from 'fyo/utils/errors';
|
||||
import { BackendResponse } from 'utils/ipc/types';
|
||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||
import { SelectFileOptions, SelectFileReturn, TemplateFile } from 'utils/types';
|
||||
import { showToast } from './interactive';
|
||||
import { showDialog, showToast } from './interactive';
|
||||
import { setLanguageMap } from './language';
|
||||
import { showMessageDialog } from './ui';
|
||||
|
||||
export function reloadWindow() {
|
||||
return ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW);
|
||||
@ -41,19 +40,22 @@ export async function deleteDb(filePath: string) {
|
||||
)) as BackendResponse;
|
||||
|
||||
if (error?.code === 'EBUSY') {
|
||||
showMessageDialog({
|
||||
message: t`Delete Failed`,
|
||||
showDialog({
|
||||
title: t`Delete Failed`,
|
||||
detail: t`Please restart and try again`,
|
||||
type: 'error',
|
||||
});
|
||||
} else if (error?.code === 'ENOENT') {
|
||||
showMessageDialog({
|
||||
message: t`Delete Failed`,
|
||||
showDialog({
|
||||
title: t`Delete Failed`,
|
||||
detail: t`File ${filePath} does not exist`,
|
||||
type: 'error',
|
||||
});
|
||||
} else if (error?.code === 'EPERM') {
|
||||
showMessageDialog({
|
||||
message: t`Cannot Delete`,
|
||||
showDialog({
|
||||
title: t`Cannot Delete`,
|
||||
detail: t`Close Frappe Books and try manually`,
|
||||
type: 'error',
|
||||
});
|
||||
} else if (error) {
|
||||
const err = new BaseError(500, error.message);
|
||||
|
@ -101,13 +101,14 @@ export type PrintValues = {
|
||||
|
||||
export interface DialogOptions {
|
||||
title: string;
|
||||
description?: string;
|
||||
type?: ToastType;
|
||||
detail?: string;
|
||||
buttons?: DialogButton[];
|
||||
}
|
||||
|
||||
export type DialogButton = {
|
||||
label: string;
|
||||
handler: () => any;
|
||||
action: () => any;
|
||||
isPrimary?: boolean;
|
||||
isEscape?: boolean;
|
||||
};
|
||||
|
@ -2,7 +2,6 @@
|
||||
* Utils to do UI stuff such as opening dialogs, toasts, etc.
|
||||
* Basically anything that may directly or indirectly import a Vue file.
|
||||
*/
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { t } from 'fyo';
|
||||
import type { Doc } from 'fyo/model/doc';
|
||||
import { Action } from 'fyo/model/types';
|
||||
@ -16,17 +15,16 @@ import { Schema } from 'schemas/types';
|
||||
import { handleErrorWithDialog } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import router from 'src/router';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
import { SelectFileOptions } from 'utils/types';
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import { stringifyCircular } from './';
|
||||
import { evaluateHidden } from './doc';
|
||||
import { showToast } from './interactive';
|
||||
import { showDialog, showToast } from './interactive';
|
||||
import { selectFile } from './ipcCalls';
|
||||
import { showSidebar } from './refs';
|
||||
import {
|
||||
ActionGroup,
|
||||
MessageDialogOptions,
|
||||
DialogButton,
|
||||
QuickEditOptions,
|
||||
SettingsTab,
|
||||
ToastOptions,
|
||||
@ -96,33 +94,6 @@ export async function openQuickEdit({
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
window.openqe = openQuickEdit;
|
||||
|
||||
export async function showMessageDialog({
|
||||
message,
|
||||
detail,
|
||||
buttons = [],
|
||||
}: MessageDialogOptions) {
|
||||
const options = {
|
||||
message,
|
||||
detail,
|
||||
buttons: buttons.map((a) => a.label),
|
||||
};
|
||||
|
||||
const { response } = (await ipcRenderer.invoke(
|
||||
IPC_ACTIONS.GET_DIALOG_RESPONSE,
|
||||
options
|
||||
)) as { response: number };
|
||||
|
||||
const button = buttons[response];
|
||||
if (!button?.action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await button.action();
|
||||
}
|
||||
|
||||
export async function openSettings(tab: SettingsTab) {
|
||||
await routeTo({ path: '/settings', query: { tab } });
|
||||
}
|
||||
@ -145,21 +116,22 @@ export async function deleteDocWithPrompt(doc: Doc) {
|
||||
detail = t`This action is permanent and will delete associated ledger entries.`;
|
||||
}
|
||||
|
||||
return await showMessageDialog({
|
||||
message: t`Delete ${getActionLabel(doc)}?`,
|
||||
return await showDialog({
|
||||
title: t`Delete ${getActionLabel(doc)}?`,
|
||||
detail,
|
||||
type: 'warning',
|
||||
buttons: [
|
||||
{
|
||||
label: t`Yes`,
|
||||
async action() {
|
||||
try {
|
||||
await doc.delete();
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (getDbError(err as Error) === LinkValidationError) {
|
||||
showMessageDialog({
|
||||
message: t`Delete Failed`,
|
||||
showDialog({
|
||||
title: t`Delete Failed`,
|
||||
detail: t`Cannot delete ${schemaLabel} ${doc.name!} because of linked entries.`,
|
||||
type: 'error',
|
||||
});
|
||||
} else {
|
||||
handleErrorWithDialog(err as Error, doc);
|
||||
@ -167,13 +139,17 @@ export async function deleteDocWithPrompt(doc: Doc) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
label: t`No`,
|
||||
action() {
|
||||
return false;
|
||||
},
|
||||
isEscape: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -211,27 +187,31 @@ export async function cancelDocWithPrompt(doc: Doc) {
|
||||
}
|
||||
}
|
||||
|
||||
return await showMessageDialog({
|
||||
message: t`Cancel ${getActionLabel(doc)}?`,
|
||||
return await showDialog({
|
||||
title: t`Cancel ${getActionLabel(doc)}?`,
|
||||
detail,
|
||||
type: 'warning',
|
||||
buttons: [
|
||||
{
|
||||
label: t`Yes`,
|
||||
async action() {
|
||||
try {
|
||||
await doc.cancel();
|
||||
return true;
|
||||
} catch (err) {
|
||||
handleErrorWithDialog(err as Error, doc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
label: t`No`,
|
||||
action() {
|
||||
return false;
|
||||
},
|
||||
isEscape: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -596,9 +576,14 @@ export async function commonDocSubmit(doc: Doc): Promise<boolean> {
|
||||
|
||||
async function showSubmitOrSyncDialog(doc: Doc, type: 'submit' | 'sync') {
|
||||
const label = getActionLabel(doc);
|
||||
let message = t`Submit ${label}?`;
|
||||
let title = t`Submit ${label}?`;
|
||||
if (type === 'sync') {
|
||||
message = t`Save ${label}?`;
|
||||
title = t`Save ${label}?`;
|
||||
}
|
||||
|
||||
let detail = t`Mark ${doc.schema.label} as submitted`;
|
||||
if (type === 'sync') {
|
||||
detail = t`Save ${doc.schema.label} to database`;
|
||||
}
|
||||
|
||||
const yesAction = async () => {
|
||||
@ -612,19 +597,22 @@ async function showSubmitOrSyncDialog(doc: Doc, type: 'submit' | 'sync') {
|
||||
return true;
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
const buttons: DialogButton[] = [
|
||||
{
|
||||
label: t`Yes`,
|
||||
action: yesAction,
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
label: t`No`,
|
||||
action: () => false,
|
||||
isEscape: true,
|
||||
},
|
||||
];
|
||||
|
||||
return await showMessageDialog({
|
||||
message,
|
||||
return await showDialog({
|
||||
title,
|
||||
detail,
|
||||
buttons,
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
reactive,
|
||||
ref,
|
||||
ref
|
||||
} from 'vue';
|
||||
import { getIsMac } from './misc';
|
||||
import { Shortcuts } from './shortcuts';
|
||||
@ -16,7 +16,7 @@ import {
|
||||
commonDocSync,
|
||||
commongDocDelete,
|
||||
showCannotCancelOrDeleteToast,
|
||||
showCannotSaveOrSubmitToast,
|
||||
showCannotSaveOrSubmitToast
|
||||
} from './ui';
|
||||
|
||||
export function useKeys() {
|
||||
@ -107,35 +107,35 @@ export function useDocShortcuts(
|
||||
context = name + '-' + Math.random().toString(36).slice(2, 6);
|
||||
}
|
||||
|
||||
const syncOrSubmitCallback = () => {
|
||||
const syncOrSubmitCallback = async () => {
|
||||
const doc = docRef.value;
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.canSave) {
|
||||
return commonDocSync(doc, true);
|
||||
return await commonDocSync(doc, true);
|
||||
}
|
||||
|
||||
if (doc.canSubmit) {
|
||||
return commonDocSubmit(doc);
|
||||
return await commonDocSubmit(doc);
|
||||
}
|
||||
|
||||
showCannotSaveOrSubmitToast(doc);
|
||||
};
|
||||
|
||||
const cancelOrDeleteCallback = () => {
|
||||
const cancelOrDeleteCallback = async () => {
|
||||
const doc = docRef.value;
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.canCancel) {
|
||||
return commonDocCancel(doc);
|
||||
return await commonDocCancel(doc);
|
||||
}
|
||||
|
||||
if (doc.canDelete) {
|
||||
return commongDocDelete(doc);
|
||||
return await commongDocDelete(doc);
|
||||
}
|
||||
|
||||
showCannotCancelOrDeleteToast(doc);
|
||||
|
Loading…
Reference in New Issue
Block a user