Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add COG children picker to forms #5279

Draft
wants to merge 46 commits into
base: issue-114-backend
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b45a031
Add COG children picker to forms
CarolineDenis Sep 17, 2024
85750a7
Chnage key
CarolineDenis Sep 17, 2024
b101753
Add useEffect to create newResource
CarolineDenis Sep 17, 2024
bb463de
Add search dialog
CarolineDenis Sep 17, 2024
ae68be1
Handle add
CarolineDenis Sep 17, 2024
f3bbf59
Typo
CarolineDenis Sep 17, 2024
51e0d44
Reset resource
CarolineDenis Sep 17, 2024
c94f559
Unhide geo tables
CarolineDenis Sep 17, 2024
551c99f
Merge remote-tracking branch 'origin/production' into issue-5185
CarolineDenis Oct 1, 2024
d2e7222
Remove unused localization
CarolineDenis Oct 1, 2024
1bfb08b
Merge branch 'issue-114-backend' into issue-5185
CarolineDenis Oct 4, 2024
1762ce3
Add cojo dialog to subview
CarolineDenis Oct 4, 2024
33a95dd
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
CarolineDenis Oct 4, 2024
487949b
Lint code with ESLint and Prettier
CarolineDenis Oct 4, 2024
8c73db3
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
CarolineDenis Oct 8, 2024
3a6805c
Check totalCount
CarolineDenis Oct 8, 2024
a99d6cc
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
CarolineDenis Oct 14, 2024
a5f0425
Change child field
CarolineDenis Oct 14, 2024
5fe7956
Research and tries
CarolineDenis Oct 15, 2024
c60941f
Add parentUrl
CarolineDenis Oct 15, 2024
6c93b08
Handle mutliple COs added at same time
CarolineDenis Oct 16, 2024
3a274cf
Remove some comments
CarolineDenis Oct 16, 2024
f9d1303
Remove duplication
CarolineDenis Oct 16, 2024
6300e57
Remove log
CarolineDenis Oct 16, 2024
656518e
Remove import
CarolineDenis Oct 16, 2024
b4a910d
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
CarolineDenis Oct 16, 2024
f411069
Set cojo on parent
CarolineDenis Oct 16, 2024
5add530
Remove code
CarolineDenis Oct 16, 2024
6c01746
Remove import
CarolineDenis Oct 17, 2024
9290602
Datamodel improvements
melton-jason Oct 17, 2024
9deb5ee
Merge branch 'issue-5185' of https://github.com/specify/specify7 into…
melton-jason Oct 17, 2024
e19d291
Remove unecessary code
CarolineDenis Oct 17, 2024
a3e4d35
Remove comment
CarolineDenis Oct 18, 2024
ee44f35
Remove condition
CarolineDenis Oct 18, 2024
d97d26e
Merge branch 'issue-114-backend' into issue-5185
melton-jason Oct 18, 2024
96d3993
Add field check for childCOJOs
CarolineDenis Oct 22, 2024
d6d03db
Add to do
CarolineDenis Oct 22, 2024
9354ab6
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
CarolineDenis Oct 22, 2024
7faa4ff
Test setting parentCojo
CarolineDenis Oct 23, 2024
e845097
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
CarolineDenis Oct 23, 2024
4450ab8
Merge remote-tracking branch 'origin/issue-114-backend' into issue-5185
sharadsw Oct 25, 2024
a97855a
Add cojo back to COG
sharadsw Oct 25, 2024
5a94c4f
Use children relationship for opening COJO dialog
sharadsw Oct 25, 2024
58c03c3
Remove unused import
sharadsw Oct 25, 2024
6c220a4
Save parentCojo on childCog
sharadsw Oct 25, 2024
40d55c1
Remove unused import
sharadsw Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ export const businessRuleDefs: MappedBusinessRuleDefs = {
},
},
},

Determination: {
fieldChecks: {
taxon: async (
Expand Down
210 changes: 109 additions & 101 deletions specifyweb/frontend/js_src/lib/components/DataModel/types.ts

Large diffs are not rendered by default.

169 changes: 169 additions & 0 deletions specifyweb/frontend/js_src/lib/components/FormCells/COJODialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import React from 'react';

import { useBooleanState } from '../../hooks/useBooleanState';
import { commonText } from '../../localization/common';
import { formsText } from '../../localization/forms';
import { localized } from '../../utils/types';
import { DataEntry } from '../Atoms/DataEntry';
import type { AnySchema } from '../DataModel/helperTypes';
import type { SpecifyResource } from '../DataModel/legacyTypes';
import type { Collection, SpecifyTable } from '../DataModel/specifyTable';
import { tables } from '../DataModel/tables';
import type {
CollectionObject,
CollectionObjectGroup,
} from '../DataModel/types';
import { ResourceView } from '../Forms/ResourceView';
import { Dialog } from '../Molecules/Dialog';
import { TableIcon } from '../Molecules/TableIcon';
import { SearchDialog } from '../SearchDialog';

export function COJODialog({
parentResource,
collection,
}: {
readonly parentResource: SpecifyResource<CollectionObjectGroup> | undefined;
readonly collection: Collection<AnySchema> | undefined;
}): JSX.Element | null {
const [isOpen, handleOpen, handleClose] = useBooleanState();
const COJOChildrenTables = [
tables.CollectionObject,
tables.CollectionObjectGroup,
];
const [state, setState] = React.useState<'Add' | 'Search' | undefined>(
undefined
);
CarolineDenis marked this conversation as resolved.
Show resolved Hide resolved
const [resourceTable, setResourceTable] = React.useState<
| SpecifyTable<CollectionObject>
| SpecifyTable<CollectionObjectGroup>
| undefined
>(undefined);
const [newResource, setNewResource] = React.useState<
| SpecifyResource<CollectionObject>
| SpecifyResource<CollectionObjectGroup>
| undefined
>(undefined);

React.useEffect(() => {
if (resourceTable !== undefined) {
const createdResource = new resourceTable.Resource() as
| SpecifyResource<CollectionObject>
| SpecifyResource<CollectionObjectGroup>;
setNewResource(createdResource);
}
}, [resourceTable]);

const handleCOJOCreation = (
selectedResource?:
| SpecifyResource<CollectionObject>
| SpecifyResource<CollectionObjectGroup>
): void => {
if (parentResource === undefined) return;

const resourceToUse = selectedResource ?? newResource;

if (resourceToUse === undefined) return;

void newResource?.save();

const newCOJO = new tables.CollectionObjectGroupJoin.Resource();
const field =
resourceToUse.specifyTable.name === 'CollectionObject'
? 'childCo'
: 'childCog';

const resourceUrl = resourceToUse.url();
const parentResourceUrl = parentResource.url();

newCOJO.set(field, resourceUrl as never);
newCOJO.set('parentCog', parentResourceUrl as never);

if (resourceToUse.specifyTable.name === 'CollectionObjectGroup') {
(resourceToUse as SpecifyResource<CollectionObjectGroup>).set(
'parentCojo',
newCOJO
);
}

collection?.add(newCOJO);
};

const handleStates = (): void => {
setState(undefined);
setResourceTable(undefined);
handleClose();
};

return (
<>
<DataEntry.Add onClick={handleOpen} />
{isOpen && (
<Dialog
buttons={commonText.cancel()}
dimensionsKey="COGChildren"
header={formsText.addCOGChildren()}
onClose={handleClose}
>
<div className="flex flex-col gap-4">
{COJOChildrenTables.map((table) => (
<div className="flex items-center gap-2" key={table.name}>
<TableIcon label name={table.name} />
{table.label}
<DataEntry.Add
onClick={(): void => {
setState('Add');
setResourceTable(table);
}}
/>
<DataEntry.Search
aria-pressed="true"
onClick={(): void => {
setState('Search');
setResourceTable(table);
}}
/>
</div>
))}
</div>
</Dialog>
)}
{state === 'Add' &&
newResource !== undefined &&
parentResource !== undefined ? (
<ResourceView
dialog="nonModal"
isDependent={false}
isSubForm={false}
resource={newResource as SpecifyResource<CollectionObject>}
onAdd={undefined}
onClose={(): void => {
setState(undefined);
handleClose();
}}
onDeleted={undefined}
onSaved={(): void => {
handleCOJOCreation();
handleStates();
}}
onSaving={undefined}
/>
) : undefined}
{state === 'Search' && parentResource !== undefined ? (
<SearchDialog
extraFilters={undefined}
forceCollection={undefined}
multiple
searchView={undefined}
table={resourceTable as SpecifyTable<CollectionObject>}
onClose={(): void => setState(undefined)}
onSelected={(selectedResources): void => {
selectedResources.forEach((selectedResource) => {
handleCOJOCreation(selectedResource);
});
handleStates();
}}
/>
) : undefined}
</>
);
}
25 changes: 21 additions & 4 deletions specifyweb/frontend/js_src/lib/components/FormCells/FormTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { backboneFieldSeparator } from '../DataModel/helpers';
import type { AnySchema } from '../DataModel/helperTypes';
import type { SpecifyResource } from '../DataModel/legacyTypes';
import type { Relationship } from '../DataModel/specifyField';
import type { SpecifyTable } from '../DataModel/specifyTable';
import type { Collection, SpecifyTable } from '../DataModel/specifyTable';
import type { CollectionObjectGroup } from '../DataModel/types';
import { FormMeta } from '../FormMeta';
import type { FormCellDefinition, SubViewSortField } from '../FormParse/cells';
import { attachmentView } from '../FormParse/webOnlyViews';
Expand All @@ -33,6 +34,7 @@ import { userPreferences } from '../Preferences/userPreferences';
import { useSearchDialog } from '../SearchDialog';
import { AttachmentPluginSkeleton } from '../SkeletonLoaders/AttachmentPlugin';
import { relationshipIsToMany } from '../WbPlanView/mappingHelpers';
import { COJODialog } from './COJODialog';
import { FormCell } from './index';

const cellToLabel = (
Expand Down Expand Up @@ -72,6 +74,7 @@ export function FormTable<SCHEMA extends AnySchema>({
onFetchMore: handleFetchMore,
isCollapsed = false,
preHeaderButtons,
collection,
}: {
readonly relationship: Relationship;
readonly isDependent: boolean;
Expand All @@ -88,6 +91,7 @@ export function FormTable<SCHEMA extends AnySchema>({
readonly onFetchMore: (() => Promise<void>) | undefined;
readonly isCollapsed: boolean | undefined;
readonly preHeaderButtons?: JSX.Element;
readonly collection: Collection<AnySchema> | undefined;
}): JSX.Element {
const [sortConfig, setSortConfig] = React.useState<
SortConfig<string> | undefined
Expand Down Expand Up @@ -449,10 +453,22 @@ export function FormTable<SCHEMA extends AnySchema>({
</DataEntry.Grid>
</div>
);

const isCOJO = relationship.relatedTable.name === 'CollectionObjectGroupJoin';
// TODO: change when upadte childCojos to children in models
const isChildCojos = relationship.name === 'childCojos';

const addButtons =
typeof handleAddResources === 'function' &&
mode !== 'view' &&
!disableAdding ? (
isCOJO && isChildCojos ? (
<COJODialog
collection={collection}
parentResource={
collection?.related as SpecifyResource<CollectionObjectGroup>
}
/>
) : typeof handleAddResources === 'function' &&
mode !== 'view' &&
!disableAdding ? (
<>
{!isDependent &&
hasTablePermission(relationship.relatedTable.name, 'read') ? (
Expand All @@ -468,6 +484,7 @@ export function FormTable<SCHEMA extends AnySchema>({
) : undefined}
</>
) : undefined;

return dialog === false ? (
<DataEntry.SubForm>
<DataEntry.SubFormHeader>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function FormTableCollection({
}}
onFetchMore={collection.isComplete() ? undefined : handleFetchMore}
{...props}
collection={collection}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type {
import type { SpecifyResource } from '../DataModel/legacyTypes';
import { useAllSaveBlockers } from '../DataModel/saveBlockers';
import type { Collection, SpecifyTable } from '../DataModel/specifyTable';
import type { CollectionObjectGroup } from '../DataModel/types';
import { COJODialog } from '../FormCells/COJODialog';
import { FormTableCollection } from '../FormCells/FormTableCollection';
import type { FormType } from '../FormParse';
import type { SubViewSortField } from '../FormParse/cells';
Expand Down Expand Up @@ -131,6 +133,10 @@ export function IntegratedRecordSelector({
const isAttachmentTable =
collection.table.specifyTable.name.includes('Attachment');

const isCOJO = relationship.relatedTable.name === 'CollectionObjectGroupJoin';
// TODO: change when upadte childCojos to children in models
const isChildCojos = relationship.name === 'childCojos';

return (
<ReadOnlyContext.Provider value={isReadOnly}>
<RecordSelectorFromCollection
Expand Down Expand Up @@ -215,35 +221,44 @@ export function IntegratedRecordSelector({
relationship.relatedTable.name,
'create'
) && typeof handleAdd === 'function' ? (
<DataEntry.Add
aria-pressed={state.type === 'AddResourceState'}
disabled={
isReadOnly ||
(isToOne && collection.models.length > 0)
}
onClick={(): void => {
const resource =
new collection.table.specifyTable.Resource();

if (
isDependent ||
viewName === relationship.relatedTable.view
) {
focusFirstField();
handleAdd([resource]);
return;
isCOJO && isChildCojos ? (
<COJODialog
collection={collection}
parentResource={
collection?.related as SpecifyResource<CollectionObjectGroup>
}
/>
) : (
<DataEntry.Add
aria-pressed={state.type === 'AddResourceState'}
disabled={
isReadOnly ||
(isToOne && collection.models.length > 0)
}
onClick={(): void => {
const resource =
new collection.table.specifyTable.Resource();

if (state.type === 'AddResourceState')
setState({ type: 'MainState' });
else
setState({
type: 'AddResourceState',
resource,
handleAdd,
});
}}
/>
if (
isDependent ||
viewName === relationship.relatedTable.view
) {
focusFirstField();
handleAdd([resource]);
return;
}

if (state.type === 'AddResourceState')
setState({ type: 'MainState' });
else
setState({
type: 'AddResourceState',
resource,
handleAdd,
});
}}
/>
)
) : undefined}
{hasTablePermission(
relationship.relatedTable.name,
Expand Down
3 changes: 3 additions & 0 deletions specifyweb/frontend/js_src/lib/localization/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1159,4 +1159,7 @@ export const formsText = createDictionary({
'ru-ru': 'Номер по каталогу Числовой',
'uk-ua': 'Каталожний номер Числовий',
},
addCOGChildren: {
'en-us': 'Add COG Children',
},
} as const);
2 changes: 1 addition & 1 deletion specifyweb/specify/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ def handle_fk_fields(collection, agent, obj, data: Dict[str, Any]) -> Tuple[List
dirty: List[FieldChangeInfo] = []
for field_name, val in items:
field = obj._meta.get_field(field_name)
if not field.many_to_one: continue
if not field.many_to_one and not field.one_to_one: continue

old_related = get_related_or_none(obj, field_name)
dependent = is_dependent_field(obj, field_name)
Expand Down
5 changes: 3 additions & 2 deletions specifyweb/specify/datamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8294,8 +8294,9 @@
relationships=[
Relationship(name='collection', type='many-to-one', required=False, relatedModelName='Collection', column='CollectionID'),
Relationship(name='cogType', type='many-to-one', required=True, relatedModelName='CollectionObjectGroupType', column='COGTypeID'),
Relationship(name='cojo', type='one-to-one', required=False, relatedModelName='CollectionObjectGroupJoin', otherSideName='childCog', dependent=True),
Relationship(name='parentCojo', type='many-to-one', required=False, relatedModelName='CollectionObjectGroupJoin',column='CollectionObjectGroupJoinID', otherSideName='collectionobjectgroup'),
Relationship(name='cojo', type='one-to-many', required=False, relatedModelName='CollectionObjectGroupJoin', otherSideName='childCog', dependent=True),
Relationship(name='childCojos', type='one-to-many',required=False, dependent=True, relatedModelName='CollectionObjectGroupJoin', otherSideName='parentCog'),
Relationship(name='createdByAgent', type='many-to-one', required=False, relatedModelName='Agent', column='CreatedByAgentID'),
Relationship(name='modifiedByAgent', type='many-to-one', required=False, relatedModelName='Agent', column='ModifiedByAgentID'),
],
Expand Down Expand Up @@ -8333,7 +8334,7 @@

],
relationships=[
Relationship(name='parentCog', type='many-to-one', required=True, relatedModelName='CollectionObjectGroup', column='ParentCOGID', otherSideName='parentcojos'),
Relationship(name='parentCog', type='many-to-one', required=True, relatedModelName='CollectionObjectGroup', column='ParentCOGID', otherSideName='childCojos'),
Relationship(name='childCog', type='one-to-one', required=False, relatedModelName='CollectionObjectGroup', column='ChildCOGID', otherSideName='cojo'),
Relationship(name='childCo', type='one-to-one', required=False, relatedModelName='CollectionObject', column='ChildCOID', otherSideName='cojo'),
Relationship(name='collectionobjectgroup', type='one-to-many',required=False, relatedModelName='CollectionObjectGroup', otherSideName='parentCojo'),
Expand Down
2 changes: 1 addition & 1 deletion specifyweb/specify/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7604,7 +7604,7 @@ class Collectionobjectgroupjoin(models.Model): # aka. CoJo or CogJoin
yesno3 = models.BooleanField(blank=True, null=True, unique=False, db_column='YesNo3', db_index=False)

# Relationships: Many-to-One
parentcog = models.ForeignKey('CollectionObjectGroup', db_column='ParentCOGID', related_name='parentcojos', null=False, on_delete=models.CASCADE)
parentcog = models.ForeignKey('CollectionObjectGroup', db_column='ParentCOGID', related_name='childcojos', null=False, on_delete=models.CASCADE)

# Relationships: One-to-One
childcog = models.OneToOneField('CollectionObjectGroup', db_column='ChildCOGID', related_name='cojo', null=True, on_delete=models.CASCADE)
Expand Down
Loading