Skip to content

Commit f0b0e0a

Browse files
author
Elise Bach
committed
Merge branch 'main' into p-7886-thread-timeline-page
2 parents dbd5cd2 + d865a5c commit f0b0e0a

File tree

10 files changed

+127
-55
lines changed

10 files changed

+127
-55
lines changed

app/page.module.css

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
.main {
22
display: flex;
33
flex-direction: column;
4-
gap: 16px;
4+
gap: 32px;
55
align-items: center;
6-
padding: 6rem;
6+
padding: 2rem;
77
min-height: 100vh;
88
}
9+
10+
.list {
11+
display: flex;
12+
flex-direction: column;
13+
gap: 24px;
14+
}

app/page.tsx

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,48 @@
11
import Navigation from "@/components/navigation";
22
import styles from "./page.module.css";
3-
import { PlainClient } from "@team-plain/typescript-sdk";
3+
import { plainClient } from "@/utils/plainClient";
4+
import { ThreadRow } from "@/components/threadRow";
5+
import { PaginationControls } from "@/components/paginationControls";
46

57
export const fetchCache = "force-no-store";
68

7-
const apiKey = process.env.PLAIN_API_KEY;
8-
if (!apiKey) {
9-
throw new Error("Please set the `PLAIN_API_KEY` environment variable");
10-
}
11-
12-
export const client = new PlainClient({
13-
apiKey,
14-
});
9+
// When adapting this example get the tenant id as part of auth or fetch it afterwards
10+
const TENANT_EXTERNAL_ID = "abcd1234";
1511

1612
export default async function Home({
17-
params,
18-
searchParams,
13+
searchParams,
1914
}: {
20-
params: { slug: string };
21-
searchParams: { [key: string]: string | string[] | undefined };
15+
searchParams: { [key: string]: string | undefined };
2216
}) {
23-
const threads = await client.getThreads({
24-
filters: {
25-
// customerIds: ["c_01J28ZQKJX9CVRXVHBMAXNSV5G"],
26-
tenantIdentifiers: [{ tenantId: "te_01J299SM3E25EJHT7JKYS6G7K5" }],
27-
},
28-
});
29-
30-
console.log(threads.data?.threads.length);
17+
const threads = await plainClient.getThreads({
18+
filters: {
19+
// If you want to only allow customers to view threads they have raised then you can filter by customerIds instead.
20+
// Note that if you provide multiple filters they are combined with AND rather than OR.
21+
// customerIds: ["c_01J28ZQKJX9CVRXVHBMAXNSV5G"],
22+
tenantIdentifiers: [{ externalId: TENANT_EXTERNAL_ID }],
23+
},
24+
after: searchParams.after as string | undefined,
25+
before: searchParams.before as string | undefined,
26+
});
3127

32-
return (
33-
<>
34-
<Navigation title="Plain Headless Portal example" />
35-
<main className={styles.main}>
36-
<div>
37-
{threads.data?.threads.map((thread) => {
38-
return (
39-
<div key={`thread-row-${thread.id}`}>
40-
<a href={`/thread/${thread.id}`}>{thread.title}</a>
41-
</div>
42-
);
43-
})}
44-
</div>
45-
</main>
46-
</>
47-
);
28+
return (
29+
<>
30+
<Navigation title="Plain Headless Portal example" />
31+
<main className={styles.main}>
32+
<h2>Support requests</h2>
33+
{threads.data && (
34+
<>
35+
<div className={styles.list}>
36+
{threads.data?.threads.map((thread) => {
37+
return (
38+
<ThreadRow thread={thread} key={`thread-row-${thread.id}`} />
39+
);
40+
})}
41+
</div>
42+
<PaginationControls pageInfo={threads.data.pageInfo} />
43+
</>
44+
)}
45+
</main>
46+
</>
47+
);
4848
}

app/thread/[threadId]/page.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,6 @@ export default async function ThreadPage({
132132
},
133133
})
134134
.then((res) => res.json())
135-
.catch((err) => {
136-
console.log(err);
137-
});
138135

139136
const thread = data.data.thread;
140137
const timelineEntries = thread.timelineEntries;

components/navigation.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import styles from "./navigation.module.css";
22

33
export default function Navigation({
4-
hasBackButton = false,
5-
title,
4+
hasBackButton = false,
5+
title,
66
}: {
7-
hasBackButton?: boolean;
8-
title: string;
7+
hasBackButton?: boolean;
8+
title: string;
99
}) {
10-
return (
11-
<nav
12-
className={styles.nav}
13-
style={{ justifyContent: hasBackButton ? "flex-start" : "center" }}
14-
>
15-
{hasBackButton && <a href="/">&lt; Go back</a>}
16-
<h1>{title}</h1>
17-
</nav>
18-
);
10+
return (
11+
<nav
12+
className={styles.nav}
13+
style={{ justifyContent: hasBackButton ? "flex-start" : "center" }}
14+
>
15+
{hasBackButton && <a href="/">&lt; Go back</a>}
16+
<h1>{title}</h1>
17+
</nav>
18+
);
1919
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.pageWrapper {
2+
display: flex;
3+
gap: 8px
4+
}

components/paginationControls.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
import { PageInfoPartsFragment } from "@team-plain/typescript-sdk";
3+
import { usePathname, useRouter } from "next/navigation";
4+
import styles from "./paginationControls.module.css";
5+
6+
export function PaginationControls({
7+
pageInfo,
8+
}: {
9+
pageInfo: PageInfoPartsFragment;
10+
}) {
11+
const router = useRouter();
12+
const pathname = usePathname();
13+
return (
14+
<div className={styles.pageWrapper}>
15+
<button
16+
onClick={() => {
17+
if (pageInfo.startCursor) {
18+
router.push(`${pathname}?before=${pageInfo.startCursor}`);
19+
}
20+
}}
21+
disabled={!pageInfo.hasPreviousPage}
22+
>
23+
Previous page
24+
</button>
25+
<button
26+
onClick={() => {
27+
if (pageInfo.endCursor) {
28+
router.push(`${pathname}?after=${pageInfo.endCursor}`);
29+
}
30+
}}
31+
disabled={!pageInfo.hasNextPage}
32+
>
33+
Next page
34+
</button>
35+
</div>
36+
);
37+
}

components/threadRow.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.row {
2+
display: flex;
3+
gap: 16px;
4+
align-items: 'center';
5+
}

components/threadRow.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { plainClient } from "@/utils/plainClient";
2+
import { ThreadPartsFragment } from "@team-plain/typescript-sdk";
3+
import styles from './threadRow.module.css';
4+
5+
export async function ThreadRow({ thread }: { thread: ThreadPartsFragment }) {
6+
const customer = await plainClient.getCustomerById({ customerId: thread.customer.id });
7+
8+
return (
9+
<a className={styles.row} href={`/thread/${thread.id}`}>
10+
<div>{customer.data?.fullName}</div><div><h3>{thread.title}</h3><div>{thread.previewText}</div></div>
11+
</a>
12+
)
13+
}

utils/createdBy.ts

Whitespace-only changes.

utils/plainClient.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { PlainClient } from "@team-plain/typescript-sdk";
2+
3+
const apiKey = process.env.PLAIN_API_KEY;
4+
if (!apiKey) {
5+
throw new Error("Please set the `PLAIN_API_KEY` environment variable");
6+
}
7+
8+
export const plainClient = new PlainClient({
9+
apiKey,
10+
});

0 commit comments

Comments
 (0)