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

Allow creating a pub without a stage #676

Merged
merged 10 commits into from
Oct 2, 2024
32 changes: 22 additions & 10 deletions core/app/components/pubs/PubEditor/PubEditorClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,14 @@ type InferFormValues<T> = T extends UseFormReturn<infer V> ? V : never;
export function PubEditorClient(props: Props) {
const hasValues = Object.keys(props.pubValues).length > 0;
const paramString = hasValues ? "update" : "create";
const runCreatePub = useServerAction(actions.createPub);
const runCreatePub = useServerAction(actions.createPubRecursive);
const runUpdatePub = useServerAction(actions.updatePub);
const availableStages = [
{ id: undefined, name: "No stage" },
...props.availableStages.map((s) => {
return { id: s.id, name: s.name };
}),
];

const path = usePathname();
const router = useRouter();
Expand Down Expand Up @@ -171,12 +177,14 @@ export function PubEditorClient(props: Props) {
}

const result = await runCreatePub({
pubId,
body: {
id: pubId,
pubTypeId: pubTypeId as PubTypesId,
stageId: stageId as StagesId,
values: enabledPubValues as Record<string, any>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't like the cast to any here, but I wasn't sure how to get these types to match up 😕

Type 'PubValues' is not assignable to type 'Record<string, Json>'.
  'string' index signatures are incompatible.
    Type 'JsonValue' is not assignable to type 'Json'.
      Type 'JsonObject' is not assignable to type 'Json'.
        Type 'JsonObject' is missing the following properties from type 'Json[]': length, pop, push, concat, and 35 more.ts(2322)
integrations.ts(97, 2): The expected type comes from property 'values' which is declared here on type 'CreatePubRequestBodyWithNullsNew | { id?: string | undefined; pubTypeId: string; parentId?: string | null | undefined; values: Record<string, Json>; assigneeId?: string | undefined; children?: (Omit<CreatePubRequestBodyWithNulls, "stageId"> & { stageId?: StagesId | undefined; })[] | undefined; stageId?: StagesId | undefined; }'

Copy link
Member

Choose a reason for hiding this comment

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

I think this is fine for now! I've had trouble with the Json and JsonValue types before, too. At some point we can do a pass to improve the types for pub values objects across the project.

},
communityId: props.communityId,
parentId: props.parentId,
pubTypeId: pubTypeId as PubTypesId,
pubValues: enabledPubValues,
stageId: stageId as StagesId,
parent: props.parentId ? { id: props.parentId } : undefined,
});
if (didSucceed(result)) {
toast({
Expand Down Expand Up @@ -245,17 +253,21 @@ export function PubEditorClient(props: Props) {
<FormDescription>Select the stage you want the pub in</FormDescription>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="sm" variant="outline">
<Button
size="sm"
variant="outline"
data-testid="stage-selector"
>
{field.value
? props.availableStages.find(
? availableStages.find(
(stage) => stage.id === field.value
)?.name
: "Select Stage"}
: "No stage"}
<ChevronDown size="16" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{props.availableStages.map((stage) => (
{availableStages.map((stage) => (
<DropdownMenuItem
key={stage.id}
onClick={() => {
Expand Down
7 changes: 7 additions & 0 deletions core/app/components/pubs/PubEditor/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ import { validatePubValuesBySchemaName } from "~/actions/_lib/validateFields";
import { db } from "~/kysely/database";
import { getLoginData } from "~/lib/auth/loginData";
import { isCommunityAdmin } from "~/lib/auth/roles";
import { createPubRecursiveNew } from "~/lib/server";
import { autoCache } from "~/lib/server/cache/autoCache";
import { autoRevalidate } from "~/lib/server/cache/autoRevalidate";
import { defineServerAction } from "~/lib/server/defineServerAction";

export const createPubRecursive = defineServerAction(async function createPubRecursive(
...[props]: Parameters<typeof createPubRecursiveNew>
) {
return createPubRecursiveNew(props);
});

export const createPub = defineServerAction(async function createPub({
communityId,
stageId,
Expand Down
2 changes: 1 addition & 1 deletion core/app/components/pubs/PubEditor/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const createPubEditorSchemaFromPubFields = (

return Type.Object<{ pubTypeId: TString; stageId: TString }>({
pubTypeId: Type.String({ format: "uuid" }),
stageId: Type.String({ format: "uuid" }),
stageId: Type.Optional(Type.String({ format: "uuid" })),
...pubFieldSchemasBySlug,
});
};
33 changes: 32 additions & 1 deletion core/playwright/pub.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ test.describe("Moving a pub", () => {
const pubsPage = new PubsPage(page, COMMUNITY_SLUG);
await pubsPage.goTo();
await page.getByTestId("pub-dropdown-button").click();
await page.getByRole("button", { name: "Edit Pub" }).click();
await page.getByRole("button", { name: "Update" }).click();
await page.getByTestId("stage-selector").click();
// Shelved is its own node in stages
await page.getByRole("menuitem", { name: "Shelved" }).click();
Expand All @@ -59,3 +59,34 @@ test.describe("Moving a pub", () => {
await expect(page.getByRole("button", { name: "Move" })).toHaveCount(0);
});
});

test.describe("Creating a pub", () => {
test("Can create a pub without a stage", async () => {
const pubsPage = new PubsPage(page, COMMUNITY_SLUG);
const title = "Pub without a stage";
await pubsPage.goTo();
await page.getByRole("button", { name: "Create" }).click();
await page.getByLabel("Title").fill(title);
await page.getByLabel("Content").fill("Some content");
await page.getByRole("button", { name: "Create Pub" }).click();
await page.getByRole("link", { name: title }).click();
await page.waitForURL(/.*\/c\/.+\/pubs\/.+/);
await expect(page.getByTestId("current-stage")).toHaveCount(0);
});

test("Can create a pub with a stage", async () => {
const pubsPage = new PubsPage(page, COMMUNITY_SLUG);
const title = "Pub with a stage";
const stage = "Submitted";
await pubsPage.goTo();
await page.getByRole("button", { name: "Create" }).click();
await page.getByLabel("Title").fill(title);
await page.getByLabel("Content").fill("Some content");
await page.getByRole("button", { name: "No stage" }).click();
await page.getByRole("menuitem", { name: stage }).click();
await page.getByRole("button", { name: "Create Pub" }).click();
await page.getByRole("link", { name: title }).click();
await page.waitForURL(/.*\/c\/.+\/pubs\/.+/);
await expect(page.getByTestId("current-stage")).toHaveText(stage);
});
});