Skip to content

Commit

Permalink
Use mudder to rank form elements and related pub values (#996)
Browse files Browse the repository at this point in the history
* Add ranks to form elements

* Add rank to pub values

* Set rank for form elements with mudder when creating new forms

* Use mudder rank instead of order in form builder

* Fix test expectation

* Sort pub values by rank

* Fix bugs in related value ranking

* Fix form builder sort bugs

* Pass transaction to getPubType

* Fix off by one error

* Condense migrations

* Set collation order at field level rather than on query

* Improve form builder readability

* Cleanup

* Make keyboard interactions work on sortable form elements

* Fix missing hover state on form elements delete/restore buttons

* Add test for form element sorting

* Set config.label on element creation for proper default label

* Actually show label changes in form builder

* Set default label correctly for relationship elements

* Set correct input component for relationship fields on form creation

* Set default config.label when adding a new element to a form

* Make sure config.relationshipConfig.component is set for relationship form elements

* Update test now that label updates are actually reflected in the form builder

* Improve UX for adding relationship form element

Clicking edit on a newly added (not saved to the db) field, then
clicking cancel will no longer delete the element. That will only happen
if you click cancel immediately after adding the new element.

The save button in the form element panel will always be enabled when
first configuring a new field input element. Previously the only way
to add a new relationship field without making an unnecessary config
edit was to click the x, since the Save button was disabled and the
cancel button would remove the element from the form.

* Prevent fallback to bad slug value when rendering unconfigured relationship field

* Make sure mudder table in migrations is 0 indexed

* chore: PR preview fixes (#1021)

* chore: set log level=debug

* chore: try removing healthcheck

* chore: add healthcheck back

* chore: attach false maybe

* chore: really not sure

* chore: try removing minio-init

* chore: empty test commit

* Sort get pubs result in js

* Fix merge mistake

* Add ranks to values in datacite test

* Allow tabbing to form element buttons

Co-authored-by: Thomas F. K. Jorna <[email protected]>

* Fix arcadia seed

* Remove unnecessary sorting

* Sort pub values in the database

* Remove rank from returned values

* Update tests to ignore return order of pub values

* Update sort test to account for new drag handle setup

---------

Co-authored-by: Eric McDaniel <[email protected]>
Co-authored-by: Thomas F. K. Jorna <[email protected]>
  • Loading branch information
3 people authored and allisonking committed Mar 6, 2025
1 parent da264fa commit f94516b
Show file tree
Hide file tree
Showing 35 changed files with 698 additions and 1,163 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/on_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}

deploy-preview:
permissions: write-all
permissions:
contents: read
deployments: write
pull-requests: write
statuses: write
runs-on: ubuntu-latest
timeout-minutes: 30
needs:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,20 @@ import type { ColumnDef } from "@tanstack/react-table";

import Link from "next/link";

import type { PubsId } from "db/public";
import { Badge } from "ui/badge";
import { DataTableColumnHeader } from "ui/data-table";
import { HoverCard, HoverCardContent, HoverCardTrigger } from "ui/hover-card";

import type { PubTitleProps } from "~/lib/pubs";
import { PubTitle } from "~/app/components/PubTitle";

export type ActionRun = {
id: string;
createdAt: Date;
actionInstance: { name: string; action: string } | null;
stage: { id: string; name: string } | null;
pub: {
id: string;
values: { field: { slug: string }; value: unknown }[] | Record<string, unknown>;
createdAt: Date;
pubType: { name: string };
title: string | null;
} | null;
pub: PubTitleProps & { id: PubsId };
result: unknown;
} & (
| {
Expand Down
16 changes: 1 addition & 15 deletions core/app/c/[communitySlug]/activity/actions/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";

import { notFound, redirect } from "next/navigation";
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/postgres";
import { jsonObjectFrom } from "kysely/helpers/postgres";

import { Capabilities, MembershipType } from "db/public";

Expand Down Expand Up @@ -76,20 +76,6 @@ export default async function Page(props: {
.selectFrom("pubs")
.select(["pubs.id", "pubs.createdAt", "pubs.title"])
.whereRef("pubs.id", "=", "action_runs.pubId")
.select((eb) =>
jsonArrayFrom(
eb
.selectFrom("pub_values")
.leftJoin("pub_fields", "pub_values.fieldId", "pub_fields.id")
.select([
"pub_values.value",
"pub_fields.name as fieldName",
"pub_fields.schemaName as schemaName",
"pub_fields.slug as fieldSlug",
])
.whereRef("pub_values.pubId", "=", "pubs.id")
).as("values")
)
.select((eb) => pubType({ eb, pubTypeIdRef: "pubs.pubTypeId" }))
).as("pub"),
jsonObjectFrom(
Expand Down
4 changes: 3 additions & 1 deletion core/app/c/[communitySlug]/forms/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { assert } from "utils";

import { isUniqueConstraintError } from "~/kysely/errors";
import { getLoginData } from "~/lib/authentication/loginData";
import { getPubType } from "~/lib/server";
import { autoRevalidate } from "~/lib/server/cache/autoRevalidate";
import { findCommunityBySlug } from "~/lib/server/community";
import { defineServerAction } from "~/lib/server/defineServerAction";
Expand All @@ -27,8 +28,9 @@ export const createForm = defineServerAction(async function createForm(
}

try {
const pubType = await getPubType(pubTypeId).executeTakeFirstOrThrow();
await autoRevalidate(
insertForm(pubTypeId, name, slug, communityId, false)
insertForm(pubType, name, slug, communityId, false)
).executeTakeFirstOrThrow();
} catch (error) {
if (isUniqueConstraintError(error)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,9 @@ const memberFields = (pubId: Expression<string>) =>
.whereRef("pub_values.pubId", "=", pubId)
.where("pub_fields.schemaName", "=", CoreSchemaType.MemberId)
.distinctOn("pub_fields.id")
.orderBy(["pub_fields.id", "pub_values.createdAt desc"])
.orderBy(["pub_fields.id", "pub_values.updatedAt desc"])
);

const pubType = (pubTypeId: Expression<string>) =>
jsonObjectFrom(getPubTypeBase().whereRef("pub_types.id", "=", pubTypeId));

export const getPubChildrenTable = (parentId: PubsId, selectedPubTypeId?: PubTypesId) => {
return autoCache(
db
Expand Down
8 changes: 5 additions & 3 deletions core/app/c/[communitySlug]/types/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { isUniqueConstraintError } from "~/kysely/errors";
import { getLoginData } from "~/lib/authentication/loginData";
import { userCan } from "~/lib/authorization/capabilities";
import { defaultFormName, defaultFormSlug } from "~/lib/form";
import { ApiError } from "~/lib/server";
import { ApiError, getPubType } from "~/lib/server";
import { autoRevalidate } from "~/lib/server/cache/autoRevalidate";
import { findCommunityBySlug } from "~/lib/server/community";
import { defineServerAction } from "~/lib/server/defineServerAction";
Expand Down Expand Up @@ -157,7 +157,7 @@ export const createPubType = defineServerAction(async function createPubType(
}
try {
await db.transaction().execute(async (trx) => {
const pubType = await autoRevalidate(
const { id: pubTypeId } = await autoRevalidate(
trx
.with("newType", (db) =>
db
Expand All @@ -180,9 +180,11 @@ export const createPubType = defineServerAction(async function createPubType(
.returning("B as id")
).executeTakeFirstOrThrow();

const pubType = await getPubType(pubTypeId, trx).executeTakeFirstOrThrow();

await autoRevalidate(
insertForm(
pubType.id,
pubType,
defaultFormName(name),
defaultFormSlug(name),
communityId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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 @@ -27,7 +28,7 @@ import { ChevronDown } from "ui/icon";
import { Input } from "ui/input";
import { cn } from "utils";

import type { ButtonElement, FormBuilderSchema } from "../types";
import type { FormBuilderSchema } from "../types";
import { useCommunity } from "../../providers/CommunityProvider";
import { useFormBuilder } from "../FormBuilderContext";
import { ButtonOption } from "../SubmissionSettings";
Expand All @@ -48,7 +49,7 @@ export const ButtonConfigurationForm = ({
// This uses the parent's form context to get the most up to date version of 'elements'
const { getValues } = useFormContext<FormBuilderSchema>();
// Derive some initial values based on the state of the parent form when this panel was opened
const { button, buttonIndex, otherButtons, numElements } = useMemo(() => {
const { button, buttonIndex, otherButtons, numElements, elements } = useMemo(() => {
const elements = getValues()["elements"];
// Because a button might not have an ID yet (if it wasn't saved to the db yet) fall back to its label as an identifier
const buttonIndex = buttonIdentifier
Expand All @@ -66,7 +67,7 @@ export const ButtonConfigurationForm = ({
e.elementId !== buttonIdentifier &&
e.label !== buttonIdentifier
);
return { button, buttonIndex, otherButtons, numElements: elements.length };
return { button, buttonIndex, otherButtons, numElements: elements.length, elements };
}, []);

const schema = z.object({
Expand Down Expand Up @@ -102,7 +103,11 @@ export const ButtonConfigurationForm = ({
const onSubmit = (values: z.infer<typeof schema>) => {
const index = buttonIndex === -1 ? numElements : buttonIndex;
update(index, {
order: null,
rank: mudder.base62.mudder(
elements[index - 1]?.rank ?? "",
elements[index + 1]?.rank ?? "",
1
)[0],
type: ElementType.button,
elementId: button?.elementId,
label: values.label,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export const InputComponentConfigurationForm = ({ index, fieldInputElement }: Pr
data-testid="save-configuration-button"
type="submit"
className="bg-blue-500 hover:bg-blue-600"
disabled={!form.formState.isDirty}
disabled={!form.formState.isDirty && fieldInputElement.configured}
>
Save
</Button>
Expand Down
25 changes: 18 additions & 7 deletions core/app/components/FormBuilder/ElementPanel/SelectElement.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import mudder from "mudder";
import { useFormContext } from "react-hook-form";
import { defaultComponent, SCHEMA_TYPES_WITH_ICONS } from "schemas";
import { defaultComponent } from "schemas";

import { ElementType, StructuralFormElement } from "db/public";
import { ElementType, InputComponent, StructuralFormElement } from "db/public";
import { Button } from "ui/button";
import { Type } from "ui/icon";
import { Input } from "ui/input";
import { usePubFieldContext } from "ui/pubFields";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "ui/tabs";
Expand All @@ -18,9 +18,9 @@ export const SelectElement = ({ panelState }: { panelState: PanelState }) => {

const { elementsCount, dispatch, addElement } = useFormBuilder();
const { getValues } = useFormContext();
const elements: FormElementData[] = getValues()["elements"];

const fieldButtons = Object.values(fields).map((field) => {
const elements: FormElementData[] = getValues()["elements"];
const usedFields = elements.map((e) => e.fieldId);
if (
usedFields.includes(field.id) ||
Expand Down Expand Up @@ -49,9 +49,16 @@ export const SelectElement = ({ panelState }: { panelState: PanelState }) => {
fieldId: field.id,
required: true,
type: ElementType.pubfield,
order: elementsCount,
rank: mudder.base62.mudder(elements[elementsCount - 1].rank, "", 1)[0],
configured: false,
label: field.name,
config: field.isRelation
? {
relationshipConfig: {
label: field.name,
component: InputComponent.relationBlock,
},
}
: { label: field.name },
component,
schemaName,
isRelation: field.isRelation,
Expand Down Expand Up @@ -130,7 +137,11 @@ export const SelectElement = ({ panelState }: { panelState: PanelState }) => {
addElement({
element: elementType,
type: ElementType.structural,
order: elementsCount,
rank: mudder.base62.mudder(
elements[elementsCount - 1].rank,
"",
1
)[0],
configured: false,
});
dispatch({
Expand Down
Loading

0 comments on commit f94516b

Please sign in to comment.