Skip to content

Commit

Permalink
Make it easier to customize form definition
Browse files Browse the repository at this point in the history
Adds an option to use autogenerated form definition rather than
the custom one (may be useful on mobile devices and narrow screens).

Also, adds a link to quickly go to the form definition.

Fixes #1330
  • Loading branch information
maxpatiiuk committed Jul 4, 2022
1 parent 2444ccf commit d45cd16
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 9 deletions.
5 changes: 4 additions & 1 deletion specifyweb/frontend/js_src/lib/cachedefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export type CacheDefinitions = {
readonly autoNumbering: {
readonly [TABLE_NAME in keyof Tables]?: RA<
TableFields<Tables[TABLE_NAME]>
>;
>;
};
readonly useCustomForm: {
readonly [TABLE_NAME in keyof Tables]?: boolean;
};
readonly carryForwardShowHidden: boolean;
};
Expand Down
93 changes: 93 additions & 0 deletions specifyweb/frontend/js_src/lib/components/formdefinition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React from 'react';

import { fetchCollection } from '../collection';
import { commonText } from '../localization/common';
import { formsText } from '../localization/forms';
import { schema } from '../schema';
import type { SpecifyModel } from '../specifymodel';
import { Button, Input, Label, Link } from './basic';
import { useAsyncState, useBooleanState } from './hooks';
import { Dialog } from './modaldialog';
import { useCachedState } from './statecache';

export function FormDefinition({
model,
}: {
readonly model: SpecifyModel;
}): JSX.Element {
const [isOpen, handleOpen, handleClose] = useBooleanState();
return (
<>
<Button.Simple onClick={handleOpen}>
{commonText('formDefinition')}
</Button.Simple>
{isOpen && <FormDefinitionDialog model={model} onClose={handleClose} />}
</>
);
}

function FormDefinitionDialog({
model,
onClose: handleClose,
}: {
readonly model: SpecifyModel;
readonly onClose: () => void;
}): JSX.Element {
const [globalConfig = {}, setGlobalConfig] = useCachedState({
category: 'forms',
key: 'useCustomForm',
defaultValue: {},
staleWhileRefresh: false,
});
const useCustomForm = globalConfig[model.name] ?? true;
const handleChange = (checked: boolean): void =>
setGlobalConfig({ ...globalConfig, [model.name]: !checked });

const viewDefinitionLink = useFormDefinition();

return (
<Dialog
header={commonText('formDefinition')}
buttons={commonText('close')}
onClose={handleClose}
>
<Label.ForCheckbox>
<Input.Checkbox checked={!useCustomForm} onValueChange={handleChange} />
{formsText('useAutoGeneratedForm')}
</Label.ForCheckbox>
{typeof viewDefinitionLink === 'string' && (
<Link.NewTab href={viewDefinitionLink}>
{formsText('editFormDefinition')}
</Link.NewTab>
)}
</Dialog>
);
}

/*
* BUG: this assumes that the ViewSet for current collection was the one used
* to render this form. That is not always the case.
*/
function useFormDefinition(): string | undefined {
const [url] = useAsyncState(
React.useCallback(
async () =>
fetchCollection(
'SpViewSetObj',
{
limit: 1,
},
{
spAppResourceDir__Collection: schema.domainLevelIds.collection,
}
)
.then(({ records }) => records[0]?.id)
.then((id) =>
typeof id === 'number' ? `/specify/viewsets/${id}/` : undefined
),
[]
),
false
);
return url;
}
2 changes: 2 additions & 0 deletions specifyweb/frontend/js_src/lib/components/formpreferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { commonText } from '../localization/common';
import { Button } from './basic';
import { FormAutoNumbering } from './formautonumbering';
import { CarryForwardButton } from './formcarryforward';
import { FormDefinition } from './formdefinition';
import { useBooleanState } from './hooks';
import { icons } from './icons';
import { Dialog } from './modaldialog';
Expand Down Expand Up @@ -49,6 +50,7 @@ function PreferencesDialog({
<div className="flex flex-wrap gap-2">
<CarryForwardButton model={resource.specifyModel} />
<FormAutoNumbering resource={resource} />
<FormDefinition model={resource.specifyModel} />
</div>
</Dialog>
);
Expand Down
2 changes: 1 addition & 1 deletion specifyweb/frontend/js_src/lib/components/preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ export const preferenceDefinitions = {
},
},
definition: {
title: preferencesText('definition'),
title: commonText('formDefinition'),
items: {
flexibleColumnWidth: defineItem<boolean>({
title: preferencesText('flexibleColumnWidth'),
Expand Down
16 changes: 13 additions & 3 deletions specifyweb/frontend/js_src/lib/components/specifyform.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { NotFoundView } from './notfoundview';
import { usePref } from './preferenceshooks';
import { loadingGif } from './queryresultstable';
import { FormCell } from './specifyformcell';
import { useCachedState } from './statecache';

/**
* By default, Specify 7 replaces all ObjectAttachment forms with
Expand All @@ -46,6 +47,13 @@ export function useViewDefinition({
readonly formType: FormType;
readonly mode: FormMode;
}): ViewDescription | undefined {
const [globalConfig = {}] = useCachedState({
category: 'forms',
key: 'useCustomForm',
defaultValue: {},
staleWhileRefresh: false,
});
const useCustomForm = globalConfig[model.name] ?? true;
const [viewDefinition] = useAsyncState<ViewDescription>(
React.useCallback(
async () =>
Expand All @@ -56,7 +64,8 @@ export function useViewDefinition({
formType,
mode,
}
: getView(
: useCustomForm
? getView(
viewName === originalAttachmentsView
? 'ObjectAttachment'
: viewName
Expand All @@ -83,8 +92,9 @@ export function useViewDefinition({
model,
})
) ?? autoGenerateViewDefinition(model, formType, mode)
),
[viewName, formType, mode, model]
)
: autoGenerateViewDefinition(model, formType, mode),
[useCustomForm, viewName, formType, mode, model]
),
false
);
Expand Down
4 changes: 4 additions & 0 deletions specifyweb/frontend/js_src/lib/localization/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1157,4 +1157,8 @@ export const commonText = createDictionary({
'en-us': 'Reveal Hidden Form Fields',
'ru-ru': 'Показать скрытые поля формы',
},
formDefinition: {
'en-us': 'Form Definition',
'ru-ru': 'Схема формы',
},
});
8 changes: 8 additions & 0 deletions specifyweb/frontend/js_src/lib/localization/forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -671,4 +671,12 @@ export const formsText = createDictionary({
'en-us': 'Auto Numbering',
'ru-ru': 'Автонумерация',
},
editFormDefinition: {
'en-us': 'Edit Form Definition',
'ru-ru': 'Редактировать схему формы',
},
useAutoGeneratedForm: {
'en-us': 'Use Auto Generated Form',
'ru-ru': 'Использовать автоматическую схему формы',
},
});
4 changes: 0 additions & 4 deletions specifyweb/frontend/js_src/lib/localization/preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,6 @@ export const preferencesText = createDictionary({
'en-us': 'Scroll wheel zoom',
'ru-ru': 'Колесо прокрутки может масштабировать',
},
definition: {
'en-us': 'Definition',
'ru-ru': 'Схема',
},
flexibleColumnWidth: {
'en-us': 'Flexible column width',
'ru-ru': 'Гибкая ширина столбцов',
Expand Down

0 comments on commit d45cd16

Please sign in to comment.