Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 501e9e59e0 | |||
| ffb1098a15 | |||
| 808ed9b15b | |||
| 232c95937d | |||
| 857117dc0f | |||
| 2f0c2f2999 | |||
| 1fc0804cbf | |||
| 4c28e211ec | |||
| c29219c492 | |||
| e22362808e | |||
| 94a6212caa | |||
| 46c897545f | |||
| 86c839e7fe | |||
| 8c49b33154 | |||
| cacbd7b400 | |||
| dc86c5487a | |||
| a3dd3660b4 | |||
| d81ecf039b | |||
| 00c4a01f53 | |||
| 7d388e50f6 | |||
| cbbc30f8b9 | |||
| 80c11abb7f | |||
| e8709d8f23 | |||
| 9ae3322224 | |||
| 880577f524 | |||
| 94e4b58341 | |||
| 1911571219 | |||
| c46de09bb5 | |||
| a3c2c78bc5 | |||
| d87715e759 | |||
| deeaa6884a | |||
| 0940396b2e | |||
| 923750a6f7 | |||
| 4de6db7720 | |||
| 657147a9bd | |||
| 14e9e9ef1c |
@@ -1,17 +0,0 @@
|
||||
# How to contribute
|
||||
|
||||
> **For questions/help, feature requests and bug reports please create an [issue](https://github.com/Daniel31x13/link-warden/issues) (please use the right lable).**
|
||||
|
||||
First off, I'm really glad you're reading this and thank you for taking the time to contribute!
|
||||
|
||||
1. Confirm your planned implementation fit into our project [features](https://github.com/Daniel31x13/link-warden#features) and [project roadmap](https://github.com/Daniel31x13/link-warden/wiki#project-roadmap) (wether it's adding a new feature or improving our existing code).
|
||||
|
||||
2. Open an [issue](https://github.com/Daniel31x13/link-warden/issues/new?assignees=&labels=contribution&template=contribution.md&title=Contribution) with your planned implementation to discuss.
|
||||
|
||||
3. Check in with me before starting development to make sure your work wont conflict with or duplicate existing work (by doing step 2).
|
||||
|
||||
4. Commit, push, and submit a PR and wait for review feedback.
|
||||
|
||||
5. Have patience, don't abandon your PR! We love contributors but we don't always have time to respond to notifications instantly. If you want a faster response, DM me on [Twitter](https://twitter.com/daniel31x13).
|
||||
|
||||
Thanks again! <3
|
||||
@@ -1,13 +1,95 @@
|
||||
<div align="center">
|
||||
<h1>
|
||||
Linkwarden
|
||||
</h1>
|
||||
<h3>Collect, Organize, and Preserve Links with Seamless Collaboration!</h3>
|
||||
<img src="./assets/icon.png" width="100px" />
|
||||
<h1>Linkwarden</h1>
|
||||
|
||||
Rebuilding things from ground up (Almost ready).
|
||||
<a href="https://discord.com/invite/CtuYV47nuJ"><img src="https://img.shields.io/discord/1117993124669702164?logo=discord&style=flat-square" alt="Discord"></a>
|
||||
<img src="https://img.shields.io/github/commit-activity/m/linkwarden/linkwarden?style=flat-square" alt="Github Activity">
|
||||
<img src="https://img.shields.io/github/languages/top/linkwarden/linkwarden?style=flat-square" alt="Top Language">
|
||||
<img src="https://img.shields.io/github/stars/linkwarden/linkwarden?style=flat-square" alt="Github Stars">
|
||||
|
||||
To get a better sense at what this repository is all about, you can check out the old version *[here](https://github.com/linkwarden/linkwarden-old)*.
|
||||
|
||||
<h3>Thanks for your patience!</h3>
|
||||
</div>
|
||||
|
||||
<div align='center'>
|
||||
|
||||
[Homepage](https://linkwarden.app) | [Getting Started](https://docs.linkwarden.app/getting-started) | [Features](https://github.com/linkwarden/linkwarden#features) | [Roadmap](https://github.com/linkwarden/linkwarden#roadmap) | [Screenshots](https://github.com/linkwarden/linkwarden#screenshots) | [Support ❤](https://github.com/linkwarden/linkwarden#support-)
|
||||
|
||||
</div>
|
||||
|
||||
## Intro & motivation
|
||||
|
||||
**Linkwarden is a self-hosted, open-source collaborative bookmark manager to collect, organize and archive webpages.** The objective is to organize useful webpages and articles you find across the web in one place, and since useful webpages can go away (see the inevitability of [Link Rot](https://www.howtogeek.com/786227/what-is-link-rot-and-how-does-it-threaten-the-web/)), Linkwarden also saves a copy of each webpage as a Screenshot and PDF, ensuring accessibility even if the original content is no longer available.
|
||||
|
||||
Additionally, Linkwarden is designed with collaboration in mind, sharing links with the public and/or allowing multiple users to work together seamlessly.
|
||||
|
||||
<img src="./assets/showcase_image.png" />
|
||||
|
||||
<details>
|
||||
<summary><b>A bit of a "history"</b></summary>
|
||||
Linkwarden has been completely rebuilt and redesigned from ground up, so pretty much the only thing it has in common with its predecessor is the idea behind it - bookmark management.
|
||||
|
||||
**What happened to the old version?**
|
||||
We highly recommend you **not** to use the old version as it is no longer maintained and has much less features. But anyway if you really wanna check it out, here it is in [this repo](https://github.com/linkwarden/linkwarden-old).
|
||||
|
||||
</details>
|
||||
|
||||
## Main Tech Stack
|
||||
|
||||
- NextJS
|
||||
- TypeScript
|
||||
- Tailwind
|
||||
- Prisma
|
||||
- Zustand
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Auto capture a screenshot and a PDF of each link.
|
||||
- ✅ Organize links by collection, name, description and multiple tags.
|
||||
- ✅ Collaborate on gathering links in a collection.
|
||||
- ✅ Customize the permissions of each member.
|
||||
- ✅ Share your collected links with the world.
|
||||
- ✅ Search, filter and sort by link details.
|
||||
- ✅ Responsive design and supports most browsers.
|
||||
|
||||
## Roadmap
|
||||
|
||||
There are _many_ upcoming features, below are only _some_ of the 100% planned ones:
|
||||
|
||||
- 🐳 Docker version.
|
||||
- 🌒 Dark mode.
|
||||
- 📦 Import/Export your data.
|
||||
- 🧩 Browser extention.
|
||||
|
||||
Also make sure to check out our [public roadmap](https://github.com/orgs/linkwarden/projects/1).
|
||||
|
||||
## Docs
|
||||
|
||||
Currently, the Documentation is a bit targeted towards a more tech-savvy audience and has so much room to improve, you can find it [here](https://docs.linkwarden.app).
|
||||
|
||||
## Development
|
||||
|
||||
If you want to contribute, Thanks! Start by checking our [public roadmap](https://github.com/orgs/linkwarden/projects/1), there you'll see a [readme item for contributers](https://github.com/orgs/linkwarden/projects/1?pane=issue&itemId=34708277) for the rest of the info on how to contribute to this repo.
|
||||
|
||||
## Security
|
||||
|
||||
If you found a security vulnerability, please do **not** create a public issue, instead send an email to [security@linkwarden.app](mailto:security@linkwarden.app) stating the vulnerability. Thanks!
|
||||
|
||||
## Screenshots
|
||||
|
||||
<img src="./assets/collections.png" />
|
||||
|
||||
<img src="./assets/collaborators.png" />
|
||||
|
||||
<img src="./assets/link_details.png" />
|
||||
|
||||
## Support ❤
|
||||
|
||||
Any [donations](https://opencollective.com/linkwarden) are highly appreciated. <3
|
||||
|
||||
Here are the other ways to support/cheer this project:
|
||||
|
||||
- Starring this repo.
|
||||
- Joining us on [Discord](https://discord.com/invite/CtuYV47nuJ).
|
||||
- Following @daniel31x13 on [Mastodon](https://mastodon.social/@daniel31x13), [Twitter](https://twitter.com/daniel31x13) and [GitHub](https://github.com/daniel31x13).
|
||||
- Referring Linkwarden to a friend.
|
||||
|
||||
If you did any of the above, Thanksss! Otherwise thanks.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 362 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 324 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 315 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 254 KiB |
@@ -11,10 +11,10 @@ type Props = {
|
||||
|
||||
export default function Modal({ toggleModal, className, children }: Props) {
|
||||
return (
|
||||
<div className="overflow-y-auto py-2 fixed top-0 bottom-0 right-0 left-0 bg-gray-500 bg-opacity-10 backdrop-blur-sm flex items-center fade-in z-30">
|
||||
<div className="overflow-y-auto py-2 fixed top-0 bottom-0 right-0 left-0 bg-gray-500 bg-opacity-10 backdrop-blur-sm flex justify-center items-center fade-in z-30">
|
||||
<ClickAwayHandler
|
||||
onClickOutside={toggleModal}
|
||||
className={`w-fit m-auto mt-10 sm:mt-20 ${className}`}
|
||||
className={`m-auto ${className}`}
|
||||
>
|
||||
<div className="slide-up relative border-sky-100 rounded-2xl border-solid border shadow-lg p-5 bg-white">
|
||||
<div
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function useInitialData() {
|
||||
const { setAccount } = useAccountStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "authenticated") {
|
||||
if (status === "authenticated" && data.user.isSubscriber) {
|
||||
setCollections();
|
||||
setTags();
|
||||
// setLinks();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import Image from "next/image";
|
||||
import React, { ReactNode } from "react";
|
||||
|
||||
interface Props {
|
||||
text?: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function CenteredForm({ text, children }: Props) {
|
||||
return (
|
||||
<div className="absolute top-0 bottom-0 left-0 right-0 flex justify-center items-center p-2">
|
||||
<div className="m-auto flex flex-col gap-2">
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={518}
|
||||
height={145}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit mx-auto"
|
||||
/>
|
||||
{text ? (
|
||||
<p className="text-lg sm:w-[30rem] w-80 mx-auto font-semibold text-black px-2 text-center">
|
||||
{text}
|
||||
</p>
|
||||
) : undefined}
|
||||
{children}
|
||||
<p className="text-center text-xs text-gray-500">
|
||||
© {new Date().getFullYear()} Linkwarden. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+52
-19
@@ -1,14 +1,21 @@
|
||||
import { GetObjectCommand, GetObjectCommandInput } from "@aws-sdk/client-s3";
|
||||
import {
|
||||
GetObjectCommand,
|
||||
GetObjectCommandInput,
|
||||
S3,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import s3Client from "./s3Client";
|
||||
import util from "util";
|
||||
|
||||
type ReturnContentTypes =
|
||||
| "text/plain"
|
||||
| "image/jpeg"
|
||||
| "image/png"
|
||||
| "application/pdf";
|
||||
|
||||
export default async function readFile({ filePath }: { filePath: string }) {
|
||||
let contentType:
|
||||
| "text/plain"
|
||||
| "image/jpeg"
|
||||
| "image/png"
|
||||
| "application/pdf";
|
||||
let contentType: ReturnContentTypes;
|
||||
|
||||
if (s3Client) {
|
||||
const bucketParams: GetObjectCommandInput = {
|
||||
@@ -17,26 +24,52 @@ export default async function readFile({ filePath }: { filePath: string }) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await s3Client.send(new GetObjectCommand(bucketParams));
|
||||
const data = await streamToBuffer(response.Body);
|
||||
let returnObject:
|
||||
| {
|
||||
file: Buffer | string;
|
||||
contentType: ReturnContentTypes;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
if (filePath.endsWith(".pdf")) {
|
||||
contentType = "application/pdf";
|
||||
} else if (filePath.endsWith(".png")) {
|
||||
contentType = "image/png";
|
||||
} else {
|
||||
// if (filePath.endsWith(".jpg"))
|
||||
contentType = "image/jpeg";
|
||||
const headObjectAsync = util.promisify(
|
||||
s3Client.headObject.bind(s3Client)
|
||||
);
|
||||
|
||||
try {
|
||||
await headObjectAsync(bucketParams);
|
||||
} catch (err) {
|
||||
contentType = "text/plain";
|
||||
|
||||
returnObject = {
|
||||
file: "File not found, it's possible that the file you're looking for either doesn't exist or hasn't been created yet.",
|
||||
contentType,
|
||||
};
|
||||
}
|
||||
|
||||
return { file: data, contentType };
|
||||
if (!returnObject) {
|
||||
const response = await (s3Client as S3).send(
|
||||
new GetObjectCommand(bucketParams)
|
||||
);
|
||||
const data = await streamToBuffer(response.Body);
|
||||
|
||||
if (filePath.endsWith(".pdf")) {
|
||||
contentType = "application/pdf";
|
||||
} else if (filePath.endsWith(".png")) {
|
||||
contentType = "image/png";
|
||||
} else {
|
||||
// if (filePath.endsWith(".jpg"))
|
||||
contentType = "image/jpeg";
|
||||
}
|
||||
returnObject = { file: data as Buffer, contentType };
|
||||
}
|
||||
|
||||
return returnObject;
|
||||
} catch (err) {
|
||||
console.log("Error", err);
|
||||
console.log("Error:", err);
|
||||
|
||||
contentType = "text/plain";
|
||||
|
||||
return {
|
||||
file: "File not found, it's possible that the file you're looking for either doesn't exist or hasn't been created yet.",
|
||||
file: "An internal occurred, please contact support.",
|
||||
contentType,
|
||||
};
|
||||
}
|
||||
|
||||
+6
-5
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/prisma-adapter": "^1.0.1",
|
||||
"@aws-sdk/client-s3": "^3.363.0",
|
||||
"@aws-sdk/client-s3": "^3.379.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
@@ -31,9 +31,10 @@
|
||||
"bcrypt": "^5.1.0",
|
||||
"colorthief": "^2.4.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"eslint": "8.44.0",
|
||||
"csstype": "^3.1.2",
|
||||
"eslint": "8.46.0",
|
||||
"eslint-config-next": "13.4.9",
|
||||
"next": "13.1.6",
|
||||
"next": "13.4.12",
|
||||
"next-auth": "^4.22.1",
|
||||
"nodemailer": "^6.9.3",
|
||||
"playwright": "^1.35.1",
|
||||
@@ -42,7 +43,7 @@
|
||||
"react-dom": "18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-image-file-resizer": "^0.4.8",
|
||||
"react-select": "^5.7.3",
|
||||
"react-select": "^5.7.4",
|
||||
"sharp": "^0.32.1",
|
||||
"stripe": "^12.13.0",
|
||||
"typescript": "4.9.4",
|
||||
@@ -53,7 +54,7 @@
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.26",
|
||||
"prisma": "^4.16.2",
|
||||
"prisma": "^5.1.0",
|
||||
"tailwindcss": "^3.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { toast } from "react-hot-toast";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/router";
|
||||
import useAccountStore from "@/store/account";
|
||||
import CenteredForm from "@/layouts/CenteredForm";
|
||||
|
||||
export default function Subscribe() {
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
@@ -15,10 +16,6 @@ export default function Subscribe() {
|
||||
|
||||
const { updateAccount, account } = useAccountStore();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(data?.user);
|
||||
}, [status]);
|
||||
|
||||
async function submitUsername() {
|
||||
setSubmitLoader(true);
|
||||
|
||||
@@ -41,16 +38,9 @@ export default function Subscribe() {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={518}
|
||||
height={145}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit mx-auto mt-10"
|
||||
/>
|
||||
<div className="p-2 mt-10 mx-auto flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<p className="text-xl text-sky-700 w-fit font-bold">
|
||||
<CenteredForm>
|
||||
<div className="p-2 mx-auto flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-2xl shadow-md border border-sky-100">
|
||||
<p className="text-2xl text-center text-black font-bold">
|
||||
Choose a Username (Last step)
|
||||
</p>
|
||||
|
||||
@@ -91,9 +81,6 @@ export default function Subscribe() {
|
||||
Sign Out
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-xs text-gray-500 my-10">
|
||||
© {new Date().getFullYear()} Linkwarden. All rights reserved.{" "}
|
||||
</p>
|
||||
</>
|
||||
</CenteredForm>
|
||||
);
|
||||
}
|
||||
|
||||
+18
-16
@@ -1,25 +1,27 @@
|
||||
import { signIn } from "next-auth/react";
|
||||
import CenteredForm from "@/layouts/CenteredForm";
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
|
||||
export default function EmailConfirmaion() {
|
||||
return (
|
||||
<div className="overflow-y-auto py-2 fixed top-0 bottom-0 right-0 left-0 bg-gray-500 bg-opacity-10 backdrop-blur-sm flex items-center fade-in z-30">
|
||||
<div className="mx-auto p-3 text-center rounded-xl border border-sky-100 shadow-lg bg-gray-100 text-sky-800">
|
||||
<p className="text-center text-2xl mb-2">Please check your email</p>
|
||||
<CenteredForm>
|
||||
<div className="p-2 sm:w-[30rem] w-80 rounded-2xl shadow-md m-auto border border-sky-100 bg-slate-50 text-sky-800">
|
||||
<p className="text-center text-xl font-bold mb-2 text-black">
|
||||
Please check your Email
|
||||
</p>
|
||||
<p>A sign in link has been sent to your email address.</p>
|
||||
<p>You can safely close this page.</p>
|
||||
{/* <div
|
||||
onClick={() =>
|
||||
signIn("email", {
|
||||
email: email,
|
||||
redirect: false,
|
||||
})
|
||||
}
|
||||
className="mx-auto font-semibold mt-2 cursor-pointer w-fit"
|
||||
>
|
||||
Resend?
|
||||
</div> */}
|
||||
|
||||
<hr className="my-5" />
|
||||
|
||||
<p className="text-sm text-gray-500 ">
|
||||
If you didn't recieve anything, go to the{" "}
|
||||
<Link href="/forgot" className="font-bold">
|
||||
Password Recovery
|
||||
</Link>{" "}
|
||||
page and enter your Email to resend the sign in link.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CenteredForm>
|
||||
);
|
||||
}
|
||||
|
||||
+15
-20
@@ -1,4 +1,5 @@
|
||||
import SubmitButton from "@/components/SubmitButton";
|
||||
import CenteredForm from "@/layouts/CenteredForm";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
@@ -38,22 +39,19 @@ export default function Forgot() {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={518}
|
||||
height={145}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit mx-auto mt-10"
|
||||
/>
|
||||
<div className="p-2 mt-10 mx-auto flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<p className="text-xl text-sky-700 w-fit font-bold">Fogot Password?</p>
|
||||
<p className="text-md text-gray-500 mt-1">
|
||||
Enter your Email so we can send you a link to recover your account.
|
||||
</p>
|
||||
<p className="text-md text-gray-500 mt-1">
|
||||
Make sure to change your password in the profile settings afterwards.
|
||||
</p>
|
||||
<CenteredForm>
|
||||
<div className="p-2 flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-2xl shadow-md border border-sky-100">
|
||||
<p className="text-2xl text-black font-bold">Password Recovery</p>
|
||||
<div>
|
||||
<p className="text-md text-black">
|
||||
Enter your Email so we can send you a link to recover your account.
|
||||
Make sure to change your password in the profile settings
|
||||
afterwards.
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
You wont get logged in if you haven't created an account yet.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-sky-700 w-fit font-semibold mb-1">Email</p>
|
||||
|
||||
@@ -78,9 +76,6 @@ export default function Forgot() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-xs text-gray-500 my-10">
|
||||
© {new Date().getFullYear()} Linkwarden. All rights reserved.{" "}
|
||||
</p>
|
||||
</>
|
||||
</CenteredForm>
|
||||
);
|
||||
}
|
||||
|
||||
+7
-18
@@ -1,4 +1,5 @@
|
||||
import SubmitButton from "@/components/SubmitButton";
|
||||
import CenteredForm from "@/layouts/CenteredForm";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
@@ -45,21 +46,12 @@ export default function Login() {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={518}
|
||||
height={145}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit mx-auto mt-10"
|
||||
/>
|
||||
<p className="text-xl font-semibold text-sky-700 px-2 text-center">
|
||||
Sign in to your account
|
||||
</p>
|
||||
<div className="p-2 my-10 mx-auto flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<p className="text-xl text-sky-700 w-fit font-bold">
|
||||
<CenteredForm text="Sign in to your account">
|
||||
<div className="p-2 flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-2xl shadow-md border border-sky-100">
|
||||
<p className="text-2xl text-black text-center font-bold">
|
||||
Enter your credentials
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p className="text-sm text-sky-700 w-fit font-semibold mb-1">
|
||||
Username
|
||||
@@ -99,7 +91,7 @@ export default function Login() {
|
||||
<SubmitButton
|
||||
onClick={loginUser}
|
||||
label="Login"
|
||||
className="mt-2 w-full text-center"
|
||||
className=" w-full text-center"
|
||||
loading={submitLoader}
|
||||
/>
|
||||
<div className="flex items-baseline gap-1 justify-center">
|
||||
@@ -109,9 +101,6 @@ export default function Login() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-xs text-gray-500 mb-10">
|
||||
© {new Date().getFullYear()} Linkwarden. All rights reserved.{" "}
|
||||
</p>
|
||||
</>
|
||||
</CenteredForm>
|
||||
);
|
||||
}
|
||||
|
||||
+38
-48
@@ -4,6 +4,7 @@ import { toast } from "react-hot-toast";
|
||||
import SubmitButton from "@/components/SubmitButton";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import CenteredForm from "@/layouts/CenteredForm";
|
||||
|
||||
const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
|
||||
|
||||
@@ -89,23 +90,17 @@ export default function Register() {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={518}
|
||||
height={145}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit mx-auto mt-10"
|
||||
/>
|
||||
<p className="text-center px-2 text-xl font-semibold text-sky-700">
|
||||
{process.env.NEXT_PUBLIC_STRIPE_IS_ACTIVE
|
||||
? `Start using our premium services with a ${
|
||||
<CenteredForm
|
||||
text={
|
||||
process.env.NEXT_PUBLIC_STRIPE_IS_ACTIVE
|
||||
? `Start using our Premium Services with a ${
|
||||
process.env.NEXT_PUBLIC_TRIAL_PERIOD_DAYS || 14
|
||||
}-day free trial!`
|
||||
: "Create a new account"}
|
||||
</p>
|
||||
<div className="p-2 mx-auto my-10 flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<p className="text-xl text-sky-700 w-fit font-bold">
|
||||
: "Create a new account"
|
||||
}
|
||||
>
|
||||
<div className="p-2 flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-2xl shadow-md border border-sky-100">
|
||||
<p className="text-2xl text-black text-center font-bold">
|
||||
Enter your details
|
||||
</p>
|
||||
<div>
|
||||
@@ -154,40 +149,38 @@ export default function Register() {
|
||||
</div>
|
||||
) : undefined}
|
||||
|
||||
<div className="flex item-center gap-2">
|
||||
<div className="w-full">
|
||||
<p className="text-sm text-sky-700 w-fit font-semibold mb-1">
|
||||
Password
|
||||
</p>
|
||||
<div className="w-full">
|
||||
<p className="text-sm text-sky-700 w-fit font-semibold mb-1">
|
||||
Password
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="••••••••••••••"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-700 duration-100"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="••••••••••••••"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-700 duration-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<p className="text-sm text-sky-700 w-fit font-semibold mb-1">
|
||||
Confirm Password
|
||||
</p>
|
||||
<div className="w-full">
|
||||
<p className="text-sm text-sky-700 w-fit font-semibold mb-1">
|
||||
Confirm Password
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="••••••••••••••"
|
||||
value={form.passwordConfirmation}
|
||||
onChange={(e) =>
|
||||
setForm({ ...form, passwordConfirmation: e.target.value })
|
||||
}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-700 duration-100"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="••••••••••••••"
|
||||
value={form.passwordConfirmation}
|
||||
onChange={(e) =>
|
||||
setForm({ ...form, passwordConfirmation: e.target.value })
|
||||
}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-700 duration-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{process.env.NEXT_PUBLIC_STRIPE_IS_ACTIVE ? (
|
||||
<>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">
|
||||
By signing up, you agree to our{" "}
|
||||
<Link href="https://linkwarden.app/tos" className="font-semibold">
|
||||
@@ -212,7 +205,7 @@ export default function Register() {
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</>
|
||||
</div>
|
||||
) : undefined}
|
||||
|
||||
<SubmitButton
|
||||
@@ -228,9 +221,6 @@ export default function Register() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-xs text-gray-500 mb-10">
|
||||
© {new Date().getFullYear()} Linkwarden. All rights reserved.{" "}
|
||||
</p>
|
||||
</>
|
||||
</CenteredForm>
|
||||
);
|
||||
}
|
||||
|
||||
+15
-22
@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/router";
|
||||
import CenteredForm from "@/layouts/CenteredForm";
|
||||
|
||||
export default function Subscribe() {
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
@@ -20,29 +21,24 @@ export default function Subscribe() {
|
||||
const res = await fetch("/api/payment");
|
||||
const data = await res.json();
|
||||
|
||||
console.log(data);
|
||||
router.push(data.response);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={518}
|
||||
height={145}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit mx-auto mt-10"
|
||||
/>
|
||||
<p className="text-xl font-semibold text-sky-700 text-center px-2">
|
||||
{process.env.NEXT_PUBLIC_TRIAL_PERIOD_DAYS || 14} days free trial, then
|
||||
${process.env.NEXT_PUBLIC_PRICING}/month afterwards
|
||||
</p>
|
||||
<div className="p-2 mt-10 mx-auto flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<CenteredForm
|
||||
text={`${
|
||||
process.env.NEXT_PUBLIC_TRIAL_PERIOD_DAYS || 14
|
||||
}-Day free trial, then $
|
||||
${process.env.NEXT_PUBLIC_PRICING}/month afterwards`}
|
||||
>
|
||||
<div className="p-2 mx-auto flex flex-col gap-3 justify-between sm:w-[30rem] w-80 bg-slate-50 rounded-2xl shadow-md border border-sky-100">
|
||||
<p className="text-2xl text-center font-bold">
|
||||
Subscribe to Linkwarden!
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p className="text-md text-gray-500 mt-1">
|
||||
You will be redirected to Stripe.
|
||||
</p>
|
||||
<p className="text-md text-gray-500 mt-1">
|
||||
<p>You will be redirected to Stripe.</p>
|
||||
<p>
|
||||
Feel free to reach out to us at{" "}
|
||||
<a className="font-semibold" href="mailto:support@linkwarden.app">
|
||||
support@linkwarden.app
|
||||
@@ -65,9 +61,6 @@ export default function Subscribe() {
|
||||
Sign Out
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-center text-xs text-gray-500 my-10">
|
||||
© {new Date().getFullYear()} Linkwarden. All rights reserved.{" "}
|
||||
</p>
|
||||
</>
|
||||
</CenteredForm>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user