Include the user who shared the task on the share landing page (#87)

* Include the user who shared the task on the share landing page

* Add sharedAt
This commit is contained in:
Matt Rubens 2025-06-09 11:04:36 -07:00 committed by GitHub
parent 8101f2026d
commit 7b3d1ba23a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 14 deletions

View file

@ -9,9 +9,10 @@ import {
createTaskShareSchema,
shareIdSchema,
} from '@/types';
import type { SharedByUser } from '@/types/task-sharing';
import type { Message } from '@/types/analytics';
import { type TaskShare, AuditLogTargetType } from '@/db';
import { client as db, taskShares } from '@/db/server';
import { client as db, taskShares, users } from '@/db/server';
import { handleError, isAuthSuccess, generateShareToken } from '@/lib/server';
import {
isValidShareToken,
@ -199,9 +200,12 @@ export async function createTaskShare(
/**
* Get task data by share token (for viewing shared tasks)
*/
export async function getTaskByShareToken(
token: string,
): Promise<{ task: TaskWithUser; messages: Message[] } | null> {
export async function getTaskByShareToken(token: string): Promise<{
task: TaskWithUser;
messages: Message[];
sharedBy: SharedByUser;
sharedAt: Date;
} | null> {
try {
const { userId, orgId } = await auth();
@ -213,16 +217,26 @@ export async function getTaskByShareToken(
return null;
}
const [share] = await db
.select()
const [shareWithUser] = await db
.select({
share: taskShares,
sharedByUser: {
id: users.id,
name: users.name,
email: users.email,
},
})
.from(taskShares)
.innerJoin(users, eq(taskShares.createdByUserId, users.id))
.where(and(eq(taskShares.shareToken, token), eq(taskShares.orgId, orgId)))
.limit(1);
if (!share) {
if (!shareWithUser) {
return null;
}
const { share, sharedByUser } = shareWithUser;
if (isShareExpired(share.expiresAt)) {
return null;
}
@ -240,7 +254,12 @@ export async function getTaskByShareToken(
const messages = await getMessages(share.taskId);
return { task, messages };
return {
task,
messages,
sharedBy: sharedByUser,
sharedAt: share.createdAt,
};
} catch (error) {
console.error(
'Error getting task by share token:',

View file

@ -26,7 +26,7 @@ export default async function SharedTaskPage({ params }: SharedTaskPageProps) {
notFound();
}
const { task, messages } = result;
const { task, messages, sharedBy, sharedAt } = result;
return (
<div className="container mx-auto py-6">
@ -35,7 +35,12 @@ export default async function SharedTaskPage({ params }: SharedTaskPageProps) {
<span>Shared Task</span>
</div>
</div>
<SharedTaskView task={task} messages={messages} />
<SharedTaskView
task={task}
messages={messages}
sharedBy={sharedBy}
sharedAt={sharedAt}
/>
</div>
);
} catch (error) {

View file

@ -1,5 +1,6 @@
import type { TaskWithUser } from '@/actions/analytics';
import type { Message } from '@/types/analytics';
import type { SharedByUser } from '@/types/task-sharing';
import { formatCurrency, formatNumber } from '@/lib/formatters';
import { generateFallbackTitle } from '@/lib/task-utils';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui';
@ -9,9 +10,16 @@ import { Messages } from '@/app/(authenticated)/usage/Messages';
type SharedTaskViewProps = {
task: TaskWithUser;
messages: Message[];
sharedBy: SharedByUser;
sharedAt: Date;
};
export const SharedTaskView = ({ task, messages }: SharedTaskViewProps) => {
export const SharedTaskView = ({
task,
messages,
sharedBy,
sharedAt,
}: SharedTaskViewProps) => {
const taskTitle = task.title || generateFallbackTitle(task);
return (
@ -23,8 +31,7 @@ export const SharedTaskView = ({ task, messages }: SharedTaskViewProps) => {
<div>
<CardTitle className="text-xl">{taskTitle}</CardTitle>
<p className="text-sm text-muted-foreground mt-1">
Shared by {task.user.name} {' '}
{new Date(task.timestamp * 1000).toLocaleDateString()}
Shared by {sharedBy.name} {sharedAt.toLocaleDateString()}
</p>
</div>
<div className="flex items-center gap-2">
@ -36,7 +43,11 @@ export const SharedTaskView = ({ task, messages }: SharedTaskViewProps) => {
</div>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Developer</p>
<p className="font-mono">{task.user.name}</p>
</div>
<div>
<p className="text-muted-foreground">Model</p>
<p className="font-mono">{task.model}</p>

View file

@ -10,3 +10,9 @@ export type CreateTaskShareRequest = z.infer<typeof createTaskShareSchema>;
export const shareIdSchema = z.string().uuid('Invalid share ID format');
export type ShareId = z.infer<typeof shareIdSchema>;
export type SharedByUser = {
id: string;
name: string;
email: string;
};