Skip to content

Commit

Permalink
feat: create a sharable investor update page (#211)
Browse files Browse the repository at this point in the history
* feat: add loading effect to profile update

* feat: update loading icon to logo icon

* feat: rendering investor updates public page

* feat: update placeholder image

* feat: minor cleanups

* chore: stopping point before render experiments, current render is broken because of existing tailwind, trying webcomponent

* feat: complete rendering investor update on a public page
  • Loading branch information
dahal committed Mar 23, 2024
1 parent caa4f7d commit a4ed448
Show file tree
Hide file tree
Showing 20 changed files with 260 additions and 31 deletions.
41 changes: 41 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
},
"devDependencies": {
"@faker-js/faker": "^8.4.0",
"@tailwindcss/typography": "^0.5.10",
"@types/eslint": "^8.56.5",
"@types/inquirer": "^9.0.7",
"@types/lodash-es": "^4.17.12",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Company" ADD COLUMN "logo" TEXT;
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ model VerificationToken {
model Company {
id String @id @default(cuid())
name String
logo String?
publicId String
incorporationType String
incorporationDate DateTime
Expand Down
4 changes: 4 additions & 0 deletions public/placeholders/company.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
122 changes: 122 additions & 0 deletions src/app/updates/[publicId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"use server";

import Link from "next/link";
import { db } from "@/server/db";
import { render } from "jsx-email";
import { dayjsExt } from "@/common/dayjs";
import { Card } from "@/components/ui/card";
import UpdateRenderer from "@/components/update/renderer";
import { Avatar, AvatarImage } from "@/components/ui/avatar";

const PublicUpdatePage = async ({
params: { publicId },
}: {
params: { publicId: string };
}) => {
const update = await db.update.findFirst({
where: {
publicId,
},

include: {
company: {
select: {
name: true,
logo: true,
},
},

author: {
select: {
title: true,
user: {
select: {
name: true,
email: true,
image: true,
},
},
},
},
},
});

if (!update) {
return {
status: 404,
redirect: "/404",
};
}

const company = update?.company;
const author = update?.author;
const html = await render(<UpdateRenderer html={update.html} />);

return (
<div className="flex min-h-screen justify-center bg-gradient-to-br from-indigo-50 via-white to-cyan-100 px-5 pb-5 pt-12">
<div className="flex flex-col">
<div className="mb-16 flex items-center gap-3">
<Avatar className="h-12 w-12 rounded">
<AvatarImage
src={
"https://pbs.twimg.com/profile_images/1686033387482464257/Dk2qBvtc_400x400.jpg" ||
"/placeholders/company.svg"
}
/>

{/* <AvatarImage src={company.logo || "/placeholders/company.svg"} /> */}
</Avatar>

<span className="text-lg font-semibold">{company.name}</span>
</div>

<div className="mb-5">
<h1 className="text-2xl font-semibold tracking-tight">
{update.title}
</h1>
<p className="text-sm text-muted-foreground">
Last updated {dayjsExt().to(update.updatedAt)}
</p>
</div>

<Card className="max-w-4xl p-10">
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10 rounded-full">
<AvatarImage
src={author.user.image || "/placeholders/user.svg"}
/>
</Avatar>

<div>
<p className="text-lg font-semibold">{author.user.name}</p>
<p className="text-sm text-muted-foreground">{author.title}</p>
</div>
</div>

<div className="mt-5">
<article
className="prose"
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
</Card>

<div className="my-10 text-center text-sm text-muted-foreground">
<p>
Powered by{" "}
<Link
href={`https://opencap.co?utm_source=${company.name}&utm_medium=updates&utm_campaign=powered_by`}
target="_blank"
rel="noopener noreferrer"
className="text-bold text-teal-500 hover:underline"
>
OpenCap
</Link>
</p>
</div>
</div>
</div>
);
};

export default PublicUpdatePage;
4 changes: 2 additions & 2 deletions src/components/dashboard/navbar/user-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export function UserDropdown({ companyPublicId }: UserDropdownProps) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-9 w-9">
<AvatarImage src={image || "/avatar.svg"} />
<Avatar className="h-9 w-9 rounded-full">
<AvatarImage src={image || "/placeholders/user.svg"} />
</Avatar>
</Button>
</DropdownMenuTrigger>
Expand Down
20 changes: 16 additions & 4 deletions src/components/member/member-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import { type z } from "zod";
import { Form, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { zodResolver } from "@hookform/resolvers/zod";
import { api } from "@/trpc/react";
import React, { useRef } from "react";
import React, { useRef, useState } from "react";
import { useToast } from "@/components/ui/use-toast";
import { useSession } from "next-auth/react";
import { uploadFile } from "@/common/uploads";
import { useRouter } from "next/navigation";
import Loading from "@/components/shared/loading";
import {
compareFormDataWithInitial,
isFileExists,
Expand All @@ -31,6 +32,7 @@ type ProfileType = {
};

export const ProfileSettings = ({ memberProfile }: ProfileType) => {
const [loading, setLoading] = useState<boolean>(false);
const { data: session, update } = useSession();
const router = useRouter();
const { toast } = useToast();
Expand Down Expand Up @@ -102,7 +104,7 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {

toast({
variant: "default",
title: "Profile changed successfully.",
title: "🎉 Successfully updated your profile",
});
},
onError: () => {
Expand Down Expand Up @@ -140,6 +142,7 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {

if (isValid) {
try {
setLoading(true);
const { imageUrl } = await handleImageUpload(file);

if (!imageUrl) {
Expand All @@ -163,6 +166,8 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {
title: "Failed uploading image.",
description: "Please try again later.",
});
} finally {
setLoading(false);
}
} else {
toast({
Expand Down Expand Up @@ -190,6 +195,7 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {
const { fullName, jobTitle, loginEmail, workEmail } = values;

try {
setLoading(true);
saveProfileMutation.mutate({
type: PayloadType.PROFILE_DATA,
payload: {
Expand All @@ -206,6 +212,8 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {
title: "Failed updating profile.",
description: "Please try again later.",
});
} finally {
setLoading(false);
}
}

Expand All @@ -214,8 +222,10 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="grid grid-cols-1 gap-x-6 gap-y-3 sm:grid-cols-6">
<div className="col-span-full flex items-center gap-x-8">
<Avatar className="h-20 w-20">
<AvatarImage src={session?.user?.image || "/avatar.svg"} />
<Avatar className="h-20 w-20 rounded-full">
<AvatarImage
src={session?.user?.image || "/placeholders/user.svg"}
/>
</Avatar>

<div className="flex items-start space-x-3">
Expand Down Expand Up @@ -293,6 +303,8 @@ export const ProfileSettings = ({ memberProfile }: ProfileType) => {
</Button>
</div>
</form>

{loading && <Loading />}
</Form>
);
};
6 changes: 4 additions & 2 deletions src/components/member/member-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ export const columns: ColumnDef<Member[number]>[] = [
accessorFn: (row) => row.user?.name,
cell: ({ row }) => (
<div className="flex">
<Avatar className="">
<AvatarImage src={row.original?.user?.image ?? "/avatar.svg"} />
<Avatar className="rounded-full">
<AvatarImage
src={row.original?.user?.image ?? "/placeholders/user.svg"}
/>
</Avatar>

<div className=" ml-2">
Expand Down
18 changes: 5 additions & 13 deletions src/components/onboarding/company-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { cn } from "@/lib/utils";
import { useToast } from "@/components/ui/use-toast";
import { type RouterOutputs } from "@/trpc/shared";
import { dayjsExt } from "@/common/dayjs";
import { Avatar, AvatarImage } from "@/components/ui/avatar";

const formSchema = ZodOnboardingMutationSchema;

Expand Down Expand Up @@ -122,19 +123,10 @@ export const CompanyForm = ({ type, data }: CompanyFormProps) => {
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<div className="col-span-full flex items-center gap-x-8">
{user?.user.image ? (
<img
src={user.user.image}
alt="Company logo"
width={50}
height={50}
className="flex-none rounded-full object-cover"
/>
) : (
<div className="h-30 w-30 flex items-center rounded-lg bg-[#CCFBF1] p-2">
<RiImageCircleFill className="m-2 h-10 w-10 flex-none shrink-0 rounded-full object-cover text-[#14B8A6]" />
</div>
)}
<Avatar className="h-20 w-20 rounded">
<AvatarImage src={"/placeholders/company.svg"} />
</Avatar>

<div>
<Button size="sm" variant={"outline"} type="button">
{type === "edit" ? "Change company logo" : "Upload company logo"}
Expand Down
4 changes: 2 additions & 2 deletions src/components/securities/options/option-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ export const columns: ColumnDef<Option[number]>[] = [
accessorFn: (row) => row?.stakeholder?.name,
cell: ({ row }) => (
<div className="flex">
<Avatar className="">
<AvatarImage src={"/avatar.svg"} />
<Avatar className="rounded-full">
<AvatarImage src={"/placeholders/user.svg"} />
</Avatar>
<div className=" ml-2 pt-2">
<p>{row?.original?.stakeholder?.name}</p>
Expand Down
Loading

0 comments on commit a4ed448

Please sign in to comment.