This is not just post, it is live post, everyday couple of times a day I share updates how I am building Ultimate Post Generator with help of ChatGPT.
Latest updates
- Update 1. Sunday 02.07 23.00 CEST -Custom generation
- Update 2. Monday 03.07 18.00 CEST -Login Page
Project I am building
Project I am working on is Open Source LinkedIn Post Generator. I am updating working on it this week.
https://www.postgenerator.app
Here full story how I build first part of it. https://dev.to/shnai0/how-i-build-my-first-open-source-project-with-chatgpt-and-nextjs-10k-users-in-24-hours-2m7n
Currently it is purely LinkedIn Post Generator wanna turn it into Ultimate post generator and improve it for sign up users. All the setting which need to sign up and see your posts there.
Update 1. Sunday 02.07 23.00 CEST
I updated the features during weekend and previous week not it is not one feature post generator.
But I added different types of generation and different prompts:
- Generate posts with custom prompts
- Generate posts via templates
- Create enhancement for your posts
- Create post ideas
Here is my button component.
import Link from "next/link";
interface CustomButtonProps {
currentTab: string | number;
}
const CustomButton = ({ currentTab }: CustomButtonProps) => {
const buttons = [
{
link: "/",
text: "Styles💃",
tooltip: "Generate posts using styles from top LinkedIn creators.",
tabName: "vibe",
},
{
link: "/custom",
text: "Custom 🏗️",
tooltip: "Use your custom prompt to generate post",
tabName: "custom",
},
{
link: "/template",
text: "Template 📋",
tooltip: "Generate post based on example",
tabName: "template",
},
{
link: "/enhancer",
text: "Enhancer 💫",
tooltip: "Enchance your post, make it shorter, longer, correct gramamr",
tabName: "enhancer",
},
{
link: "/ideas",
text: "Ideas💡",
tooltip: "Generate ideas for your post",
tabName: "ideas",
},
];
return (
<>
{buttons.map((button, index) => (
<Link href={button.link} key={index}>
<div className="relative group">
<button
className={`px-3 py-2 rounded-md text-xs font-medium ${
currentTab === button.tabName
? "bg-gray-300 text-black"
: "border border-gray-300 bg-white text-gray-700 shadow-sm hover:bg-gray-50"
}`}
>
{button.text}
</button>
<span
className="tooltip-text text-sm bg-gray-100 text-gray-700 p-1 rounded-md absolute bottom-full mb-2 left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition duration-300"
style={{ width: "150px" }}
>
{button.tooltip}
</span>
</div>
</Link>
))}
</>
);
};
export default CustomButton;
Update 2. Monday 03.07 18.00 CEST - Login Page
Step 1. I built login page
Step 2. Next authentication file
Step 3. Prisma. prisma schema for it
Step 4. Also connected all API. keys, to Prisma DB and next Auth
import { signIn } from "next-auth/react";
import { useRouter } from "next/router";
export default function Login() {
const router = useRouter();
const { next } = router.query as { next?: string };
return (
<div className="flex h-screen w-screen justify-center">
<div
className="absolute inset-x-0 top-10 -z-10 flex transform-gpu justify-center overflow-hidden blur-3xl"
aria-hidden="true"
>
<div
className="aspect-[1108/632] w-[69.25rem] flex-none bg-gradient-to-r from-[#80caff] to-[#4f46e5] opacity-20"
style={{
clipPath:
"polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)",
}}
/>
</div>
<div className="z-10 mt-[calc(30vh)] h-fit w-full max-w-md overflow-hidden border border-blue-900 rounded-lg sm:shadow-xl">
<div className="flex flex-col items-center justify-center space-y-3 bg-blue-900 px-4 py-6 pt-8 text-center sm:px-16">
<h3 className="text-xl text-white font-semibold">
Sign in to Linkedin Post Generator
</h3>
<p className="text-sm text-gray-400">
Start generating posts with no limitations
</p>
</div>
<div className="flex flex-col bg-blue-900 px-4 py-8 sm:px-16">
<button
onClick={() => {
signIn("google", {
...(next && next.length > 0 ? { callbackUrl: next } : {}),
});
}}
className="rounded px-10 py-2 font-medium transition-colors text-gray-900 bg-gray-100 hover:text-gray-100 hover:bg-gray-500"
>
Continue with Google
</button>
</div>
</div>
</div>
);
}
Login Page
Update 3. Tuesday 03.07 18.00 CEST - Generation of posts from templates
So I spend all day, figuring out how to build the format that way that there the template is taken and from this template the post can be generated for your own topic.
I messed up with variables in my component. So I needed to change it all.
return (
<>
<div className="w-full">
<textarea
maxLength={10000}
onChange={(e) => {
setInputData(e.target.value);
processData(e.target.value); // process the text
}}
value={selectedTemplate ? selectedTemplate.text : inputData}
placeholder="Add existing post which you wrote before so it would be clear which format you are writing in and can write the same"
className="text-black w-full h-32 p-2 text-s bg-white border border-gray-300 rounded-md shadow-inner md:h-240"
/>
{/* {selectedTemplate && (
<img src={selectedTemplate.img} alt={selectedTemplate.name} />
)} */}
</div>
<Menu as="div" className="relative block text-left w-full">
<div>
<Menu.Button className="inline-flex w-full justify-between items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue">
{selectedTemplate ? selectedTemplate.name : "Select template"}
<ChevronUpIcon
className="-mr-1 ml-2 h-5 w-5 ui-open:hidden"
aria-hidden="true"
/>
<ChevronDownIcon
className="-mr-1 ml-2 h-5 w-5 hidden ui-open:block"
aria-hidden="true"
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute left-0 z-10 mt-2 w-full origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="">
{templates.map((template) => (
<Menu.Item key={template.name}>
{({ active }) => (
<button
onMouseEnter={handleMouseEnter(template)}
onMouseLeave={handleMouseLeave}
onClick={() => {
setInputData(template.text);
setSelectedTemplate(template);
processData(template.text); // process the text
}}
className="px-4 py-2 text-sm w-full text-left flex items-center space-x-2 justify-between"
>
<span>{template.name}</span>
{selectedTemplate === template && (
<CheckIcon className="w-4 h-4 text-bold" />
)}
</button>
)}
</Menu.Item>
))}
</div>
</Menu.Items>
</Transition>
</Menu>
{showImage && previewTemplate && (
<div className="absolute top-4/6 left-2/3 transform -translate-x-1/3 -translate-y-1/2 w-256 h-256">
<img
className="w-full h-full object-scale-down"
src={previewTemplate.img}
alt={previewTemplate.name}
/>
</div>
)}
</>
);
};
I am gonna share more updates today.
https://github.com/shnai0/linkedin-post-generator
All contributions are welcome here, it is an open source project:)
Top comments (3)
Great stuff! I like seeing you literally "build in public" here.
haahah Trying my best.
I am not yet ready to literally sit in front of camera as i am always so distracted
Waiting for more updates..