Skip to content

Allow ordering of related pub values #1014

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

Merged
merged 15 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions core/actions/datacite/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const pub = {
updatedAt: new Date(),
schemaName: CoreSchemaType.String,
relatedPubId: null,
rank: "a",
},
{
id: "" as PubValuesId,
Expand All @@ -86,6 +87,7 @@ const pub = {
updatedAt: new Date(),
schemaName: CoreSchemaType.String,
relatedPubId: null,
rank: "b",
},
{
id: "" as PubValuesId,
Expand All @@ -97,6 +99,7 @@ const pub = {
updatedAt: new Date(),
schemaName: CoreSchemaType.URL,
relatedPubId: null,
rank: "c",
},
{
id: "" as PubValuesId,
Expand All @@ -108,6 +111,7 @@ const pub = {
updatedAt: new Date(),
schemaName: CoreSchemaType.DateTime,
relatedPubId: null,
rank: "d",
},
],
communityId: "" as CommunitiesId,
Expand Down
4 changes: 2 additions & 2 deletions core/actions/googleDriveImport/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const run = defineRun<typeof action>(
(value) => value.fieldSlug === `${communitySlug}:publication-date`
)[0];
const publicationDate: Date = publicationDateField
? (publicationDateField.value as Date)
? (publicationDateField.value as unknown as Date)
: new Date(values.relatedPub!.createdAt);
return { [`${publicationDate.toISOString()}`]: values.relatedPubId };
});
Expand All @@ -102,7 +102,7 @@ export const run = defineRun<typeof action>(
(value) => value.fieldSlug === `${communitySlug}:publication-date`
)[0];
const publicationDate: Date = publicationDateField
? (publicationDateField.value as Date)
? (publicationDateField.value as unknown as Date)
: new Date(values.relatedPub!.createdAt);
return publicationDate.toISOString();
});
Expand Down
8 changes: 8 additions & 0 deletions core/actions/move/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import type { StagesId } from "db/public";
import { logger } from "logger";

import type { action } from "./action";
import { isUniqueConstraintError } from "~/kysely/errors";
import { movePub } from "~/lib/server/stages";
import { defineRun } from "../types";

export const run = defineRun<typeof action>(async ({ pub, config }) => {
try {
await movePub(pub.id, config.stage as StagesId).execute();
} catch (error) {
if (isUniqueConstraintError(error)) {
return {
success: true,
report: `Pub was already in stage ${config.stage}`,
data: {},
};
}
logger.error({ msg: "move", error });
return {
title: "Failed to move pub",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const move = defineServerAction(async function move(
}

try {
await movePub(pubId, destinationStageId).executeTakeFirstOrThrow();
await movePub(pubId, destinationStageId).execute();
} catch {
return { error: "The Pub was not successully moved" };
}
Expand Down
6 changes: 4 additions & 2 deletions core/app/components/ContextEditor/ContextEditorClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import dynamic from "next/dynamic";
import type { PubsId, PubTypesId } from "db/public";
import { Skeleton } from "ui/skeleton";

import type { GetPubsResult, GetPubTypesResult } from "~/lib/server";
import type { GetPubTypesResult } from "~/lib/server";
import { upload } from "../forms/actions";
import { ContextAtom } from "./AtomRenderer";

import "context-editor/style.css";

import type { ContextEditorPub } from "./ContextEditorContext";

const ContextEditor = dynamic(() => import("context-editor").then((mod) => mod.ContextEditor), {
ssr: false,
loading: () => <Skeleton className="h-16 w-full" />,
Expand All @@ -28,7 +30,7 @@ export const ContextEditorClient = ({
disabled,
hideMenu,
}: {
pubs: GetPubsResult;
pubs: ContextEditorPub[];
pubTypes: GetPubTypesResult;
pubId: PubsId;
pubTypeId: PubTypesId;
Expand Down
19 changes: 10 additions & 9 deletions core/app/components/ContextEditor/ContextEditorContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import { createContext, useContext, useState } from "react";
import type { ProcessedPub } from "contracts";
import type { PubsId, PubTypesId } from "db/public";

import type { GetPubsResult, GetPubTypesResult } from "~/lib/server";
import { processedPubsToPubsResult } from "~/lib/pubs";
import type { GetPubTypesResult } from "~/lib/server";

export type ContextEditorContext = {
pubs: GetPubsResult;
pubs: ContextEditorPub[];
pubTypes: GetPubTypesResult;
pubId?: PubsId;
pubTypeId?: PubTypesId;
Expand All @@ -22,21 +21,23 @@ const ContextEditorContext = createContext<ContextEditorContext>({
pubTypes: [],
});

type InputPub = ProcessedPub<{ withStage: true; withLegacyAssignee: true; withPubType: true }>;
export type ContextEditorPub = ProcessedPub<{
withStage: true;
withLegacyAssignee: true;
withPubType: true;
}>;
type Props = PropsWithChildren<
Omit<ContextEditorContext, "pubs"> & {
pubs: InputPub[];
pubs: ContextEditorPub[];
}
>;

export const ContextEditorContextProvider = (props: Props) => {
const [cachedPubId] = useState(props.pubId);
const { children, pubId, ...value } = props;

const contextPubs = processedPubsToPubsResult(value.pubs);
const { children, pubId, pubs, ...value } = props;

return (
<ContextEditorContext.Provider value={{ ...value, pubs: contextPubs, pubId: cachedPubId }}>
<ContextEditorContext.Provider value={{ ...value, pubs, pubId: cachedPubId }}>
{children}
</ContextEditorContext.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useMemo } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import mudder from "mudder";
import { useForm, useFormContext } from "react-hook-form";
import { z } from "zod";

Expand Down Expand Up @@ -29,6 +28,7 @@ import { Input } from "ui/input";
import { cn } from "utils";

import type { FormBuilderSchema } from "../types";
import { findRanksBetween } from "~/lib/rank";
import { useCommunity } from "../../providers/CommunityProvider";
import { useFormBuilder } from "../FormBuilderContext";
import { ButtonOption } from "../SubmissionSettings";
Expand Down Expand Up @@ -103,11 +103,10 @@ export const ButtonConfigurationForm = ({
const onSubmit = (values: z.infer<typeof schema>) => {
const index = buttonIndex === -1 ? numElements : buttonIndex;
update(index, {
rank: mudder.base62.mudder(
elements[index - 1]?.rank ?? "",
elements[index + 1]?.rank ?? "",
1
)[0],
rank: findRanksBetween({
start: elements[index - 1]?.rank ?? "",
end: elements[index + 1]?.rank ?? "",
})[0],
type: ElementType.button,
elementId: button?.elementId,
label: values.label,
Expand Down
12 changes: 5 additions & 7 deletions core/app/components/FormBuilder/ElementPanel/SelectElement.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import mudder from "mudder";
import { useFormContext } from "react-hook-form";
import { defaultComponent } from "schemas";

Expand All @@ -9,6 +8,7 @@ import { usePubFieldContext } from "ui/pubFields";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "ui/tabs";

import type { FormElementData, PanelState } from "../types";
import { findRanksBetween } from "~/lib/rank";
import { FieldIcon } from "../FieldIcon";
import { useFormBuilder } from "../FormBuilderContext";
import { structuralElements } from "../StructuralElements";
Expand Down Expand Up @@ -49,7 +49,7 @@ export const SelectElement = ({ panelState }: { panelState: PanelState }) => {
fieldId: field.id,
required: true,
type: ElementType.pubfield,
rank: mudder.base62.mudder(elements[elementsCount - 1]?.rank, "", 1)[0],
rank: findRanksBetween({ start: elements[elementsCount - 1]?.rank })[0],
configured: false,
config: field.isRelation
? {
Expand Down Expand Up @@ -137,11 +137,9 @@ export const SelectElement = ({ panelState }: { panelState: PanelState }) => {
addElement({
element: elementType,
type: ElementType.structural,
rank: mudder.base62.mudder(
elements[elementsCount - 1]?.rank,
"",
1
)[0],
rank: findRanksBetween({
start: elements[elementsCount - 1]?.rank,
})[0],
configured: false,
});
dispatch({
Expand Down
38 changes: 10 additions & 28 deletions core/app/components/FormBuilder/FormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { zodResolver } from "@hookform/resolvers/zod";
import mudder from "mudder";
import { useFieldArray, useForm } from "react-hook-form";

import type { Stages } from "db/public";
Expand All @@ -27,6 +26,7 @@ import { toast } from "ui/use-toast";

import type { FormBuilderSchema, FormElementData, PanelEvent, PanelState } from "./types";
import type { Form as PubForm } from "~/lib/server/form";
import { getRankAndIndexChanges } from "~/lib/rank";
import { renderWithPubTokens } from "~/lib/server/render/pub/renderWithPubTokens";
import { didSucceed, useServerAction } from "~/lib/serverActions";
import { PanelHeader, PanelWrapper, SidePanel } from "../SidePanel";
Expand Down Expand Up @@ -206,33 +206,15 @@ export function FormBuilder({ pubForm, id, stages }: Props) {
// Update ranks and rhf field array position when elements are dragged
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (over && active.id !== over?.id) {
// activeIndex is the position the element started at and over is where it was dropped
const activeIndex = active.data.current?.sortable?.index;
const overIndex = over.data.current?.sortable?.index;
if (activeIndex !== undefined && overIndex !== undefined) {
// "earlier" means towards the beginning of the list, or towards the top of the page
const isMovedEarlier = activeIndex > overIndex;
const activeElem = elements[activeIndex];

// When moving an element earlier in the array, find a rank between the rank of the
// element at the dropped position and the element before it. When moving an element
// later, instead find a rank between that element and the element after it
const aboveRank =
elements[isMovedEarlier ? overIndex : overIndex + 1]?.rank ?? "";
const belowRank =
elements[isMovedEarlier ? overIndex - 1 : overIndex]?.rank ?? "";
const [rank] = mudder.base62.mudder(belowRank, aboveRank, 1);

// move doesn't trigger a rerender, so it's safe to chain these calls
move(activeIndex, overIndex);
update(overIndex, {
...activeElem,
rank,
updated: true,
});
}
const changes = getRankAndIndexChanges(event, elements);
if (changes) {
// move doesn't trigger a rerender, so it's safe to chain these calls
move(changes.activeIndex, changes.overIndex);
update(changes.overIndex, {
...elements[changes.activeIndex],
rank: changes.rank,
updated: true,
});
}
},
[elements]
Expand Down
4 changes: 3 additions & 1 deletion core/app/components/FormBuilder/FormElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ export const FieldInputElement = ({ element, isEditing, labelId }: FieldInputEle
id={labelId}
className={cn("font-semibold", element.deleted ? "text-gray-500" : "")}
>
{(element.config as any)?.label ?? field.name}
{(element.config as any)?.relationshipConfig?.label ??
(element.config as any)?.label ??
field.name}
</div>
</div>
</>
Expand Down
13 changes: 6 additions & 7 deletions core/app/components/forms/AddRelatedPubsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import type { ColumnDef } from "@tanstack/react-table";
import { useRef, useState } from "react";

import type { PubsId } from "db/public";
import { pubFieldsIdSchema, pubsIdSchema } from "db/public";
import { Button } from "ui/button";
import { Checkbox } from "ui/checkbox";
import { DataTableColumnHeader } from "ui/data-table";

import type { GetPubsResult } from "~/lib/server";
import type { ContextEditorPub } from "../ContextEditor/ContextEditorContext";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fine for now, but just leaving a note that I'm unsure if we'll want to keep using ContextEditorPub here— AddRelatedPubsPanel was using the same context editor because it was already there and I needed a way to grab other pubs so figured I'd reuse it. we'll need to update the pubs here soon to paginate so that might change this type then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. i just made this change because GetPubsResult is even more deprecated, but i agree we should use a new type!

import { PanelHeader, SidePanel } from "~/app/components/SidePanel";
import { getPubTitle } from "~/lib/pubs";
import { DataTable } from "../DataTable/v2/DataTable";
Expand Down Expand Up @@ -51,7 +50,7 @@ const getColumns = () =>
);
},
},
] as const satisfies ColumnDef<GetPubsResult[number], unknown>[];
] as const satisfies ColumnDef<ContextEditorPub, unknown>[];

export const AddRelatedPubsPanel = ({
title,
Expand All @@ -61,8 +60,8 @@ export const AddRelatedPubsPanel = ({
}: {
title: string;
onCancel: () => void;
onAdd: (pubs: GetPubsResult) => void;
pubs: GetPubsResult;
onAdd: (pubs: ContextEditorPub[]) => void;
pubs: ContextEditorPub[];
}) => {
const sidebarRef = useRef(null);
const [selected, setSelected] = useState<Record<string, boolean>>({});
Expand All @@ -77,7 +76,7 @@ export const AddRelatedPubsPanel = ({
};

return (
<SidePanel ref={sidebarRef} className="justify-between">
<SidePanel ref={sidebarRef}>
<div className="flex flex-col gap-2">
<PanelHeader title={title} showCancel onCancel={onCancel} />
<DataTable
Expand All @@ -88,7 +87,7 @@ export const AddRelatedPubsPanel = ({
getRowId={(d) => d.id}
/>
</div>
<div className="flex w-full justify-between gap-2">
<div className="mt-auto flex w-full justify-between gap-2">
<Button type="button" variant="outline" className="flex-1" onClick={onCancel}>
Cancel
</Button>
Expand Down
Loading
Loading