mirror of https://github.com/BRAVO68WEB/shx.git
Merge pull request #99 from BRAVO68WEB/feature/api-integration
Feature/api integration
This commit is contained in:
commit
9525eb5202
|
@ -0,0 +1,39 @@
|
|||
import { Axios } from 'axios';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
export class ApiKeys {
|
||||
axios: Axios;
|
||||
constructor(axios: Axios) {
|
||||
this.axios = axios;
|
||||
}
|
||||
private async getMasterKey() {
|
||||
if (typeof window === 'undefined') {
|
||||
const { cookies } = await import('next/headers');
|
||||
return cookies().get('masterKey')?.value ?? '';
|
||||
} else {
|
||||
return Cookies.get('masterKey') ?? '';
|
||||
}
|
||||
}
|
||||
async getAllKeys() {
|
||||
const res = await this.axios.get(`/apiKey`, {
|
||||
params: { masterkey: await this.getMasterKey() },
|
||||
});
|
||||
return res.data.data as IApiKey[];
|
||||
}
|
||||
async createKey() {
|
||||
const res = await this.axios.post(
|
||||
`/apiKey`,
|
||||
{},
|
||||
{ params: { masterkey: await this.getMasterKey() } }
|
||||
);
|
||||
return res.data.data.key as string;
|
||||
}
|
||||
async disableApiKey(id: string) {
|
||||
const res = await this.axios.delete('/apiKey', {
|
||||
params: { masterkey: await this.getMasterKey(), apikeyID: id },
|
||||
});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiKeys;
|
|
@ -1,33 +1,45 @@
|
|||
import axios, { Axios } from 'axios';
|
||||
import Uploads from './uploads';
|
||||
import Notes from './notes';
|
||||
import ApiKeys from './apiKeys';
|
||||
import Url from './url';
|
||||
import Settings from './settings';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
class ApiSdk {
|
||||
private _axios: Axios;
|
||||
private _apiKey: string;
|
||||
private _instanceUrl: string;
|
||||
uploads: Uploads;
|
||||
notes: Notes;
|
||||
apiKeys: ApiKeys;
|
||||
url: Url;
|
||||
settings: Settings;
|
||||
constructor() {
|
||||
this._instanceUrl = process.env.NEXT_APP_API_URL as string;
|
||||
this._apiKey = '';
|
||||
this._axios = axios.create()
|
||||
this._axios = this.createAxios();
|
||||
this.uploads = new Uploads(this._axios);
|
||||
this.notes = new Notes(this._axios);
|
||||
this.apiKeys = new ApiKeys(this._axios);
|
||||
this.url = new Url(this._axios);
|
||||
this.settings = new Settings(this._axios);
|
||||
}
|
||||
private _setAxios() {
|
||||
this._axios.defaults.baseURL = this._instanceUrl
|
||||
const headers:any = this._axios.defaults.headers
|
||||
headers['x-shx-api-key'] = this._apiKey
|
||||
private createAxios(): Axios {
|
||||
const ax = axios.create();
|
||||
ax.interceptors.request.use(async config => {
|
||||
if (typeof window === 'undefined') {
|
||||
const { cookies } = await import('next/headers');
|
||||
config.headers['x-shx-api-key'] = cookies().get('apiKey')?.value;
|
||||
config.baseURL = cookies().get('instanceUrl')?.value;
|
||||
} else {
|
||||
config.headers['x-shx-api-key'] = Cookies.get().apiKey ?? '';
|
||||
config.baseURL = Cookies.get('instanceUrl') ?? '';
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
return ax;
|
||||
}
|
||||
getAxios() {
|
||||
return this._axios;
|
||||
}
|
||||
setInstanceUrl(instanceUrl: string) {
|
||||
this._instanceUrl = instanceUrl;
|
||||
this._setAxios();
|
||||
}
|
||||
setApiKey(apiKey: string) {
|
||||
this._apiKey = apiKey;
|
||||
this._setAxios();
|
||||
}
|
||||
}
|
||||
|
||||
export default new ApiSdk();
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { AddNoteType } from '@/lib/validators/notes';
|
||||
import { Axios } from 'axios';
|
||||
|
||||
export class Notes {
|
||||
axios: Axios;
|
||||
constructor(axios: Axios) {
|
||||
this.axios = axios;
|
||||
}
|
||||
async getAllNotes(search?:string) {
|
||||
const res = await this.axios.get('/gist',{params:{search}});
|
||||
const data = res.data.data as INote[];
|
||||
return data;
|
||||
}
|
||||
async uploadSingleNote(data: AddNoteType) {
|
||||
console.log("uploading")
|
||||
if(!data.passkey){
|
||||
delete data.passkey
|
||||
}
|
||||
const res = await this.axios.post('/gist', data);
|
||||
return res.data.data as INote;
|
||||
}
|
||||
async deleteSingleNote({
|
||||
noteID,
|
||||
}: {
|
||||
noteID: string;
|
||||
}) {
|
||||
const res = await this.axios.delete(`/gist/${noteID}`);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export default Notes;
|
|
@ -0,0 +1,19 @@
|
|||
import { Axios } from 'axios';
|
||||
|
||||
export class Settings {
|
||||
axios: Axios;
|
||||
constructor(axios: Axios) {
|
||||
this.axios = axios;
|
||||
}
|
||||
async getCurrentSettings() {
|
||||
const res = await this.axios.get('/settings');
|
||||
const data = res.data.data as ISettings;
|
||||
return data;
|
||||
}
|
||||
async updateASetting(key: string, value:string|string[]){
|
||||
const res = await this.axios.post('/settings', {key, value});
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
export default Settings;
|
|
@ -5,11 +5,21 @@ export class Uploads {
|
|||
constructor(axios: Axios) {
|
||||
this.axios = axios;
|
||||
}
|
||||
async getAllUploads() {
|
||||
const res = await this.axios.get('/upload');
|
||||
const data = res.data as IFile[];
|
||||
async getAllUploads(search?: string){
|
||||
const res = await this.axios.get('/upload',{params:{query:search}});
|
||||
const data = res.data.data as IFile[];
|
||||
return data;
|
||||
}
|
||||
async uploadSingleFile({file}:{file:File}){
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
const res = await this.axios.post('/upload/file', data);
|
||||
return res.data.data as IFile
|
||||
}
|
||||
async deleteSingleFile({fileID,deleteToken}:{fileID:string,deleteToken:string}){
|
||||
const res = await this.axios.get(`/upload/delete/${fileID}`,{params:{token:deleteToken}})
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
export default Uploads;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { Axios } from 'axios';
|
||||
|
||||
export class Url {
|
||||
axios: Axios;
|
||||
constructor(axios: Axios) {
|
||||
this.axios = axios;
|
||||
}
|
||||
async getAllUrls() {
|
||||
const res = await this.axios.get('/url');
|
||||
const data = res.data.data as IUrl[];
|
||||
return data;
|
||||
}
|
||||
async uploadUrl(url: string) {
|
||||
const res = await this.axios.post('/url', { url });
|
||||
return res.data.data as IUrl;
|
||||
}
|
||||
async editUrl({
|
||||
original_url,
|
||||
short_key,
|
||||
id,
|
||||
}: {
|
||||
id: string;
|
||||
original_url: string;
|
||||
short_key: string;
|
||||
}) {
|
||||
const res = await this.axios.put(`/url/${id}`, { original_url, short_key });
|
||||
return res
|
||||
}
|
||||
async deleteUrl(id: string) {
|
||||
const res = await this.axios.delete(`/url/${id}`);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export default Url;
|
|
@ -0,0 +1,9 @@
|
|||
"use client"
|
||||
|
||||
import React from 'react';
|
||||
|
||||
function ErrorPage() {
|
||||
return <div>ErrorPage</div>;
|
||||
}
|
||||
|
||||
export default ErrorPage;
|
|
@ -1,21 +1,23 @@
|
|||
import ApiKeyList from '@/components/Lists/ApiKeyList'
|
||||
import Button from '@/components/ui/Button'
|
||||
import { Plus } from 'lucide-react'
|
||||
import React from 'react'
|
||||
import api from '@/api';
|
||||
import ApiKeyList from '@/components/Lists/ApiKeyList';
|
||||
import Button from '@/components/ui/Button';
|
||||
import { Plus } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<div className=''>
|
||||
<h1 className='text-4xl'>
|
||||
Your Api Keys
|
||||
</h1>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Button className='w-min text-lg px-4 flex items-center gap-2'>Add <Plus /></Button>
|
||||
<ApiKeyList />
|
||||
</div>
|
||||
async function Page() {
|
||||
const res = await api.apiKeys.getAllKeys();
|
||||
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className="">
|
||||
<h1 className="text-4xl">Your Api Keys</h1>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Button className="w-min text-lg px-4 flex items-center gap-2">
|
||||
Add <Plus />
|
||||
</Button>
|
||||
<ApiKeyList data={res} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page
|
||||
export default Page;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
"use client"
|
||||
|
||||
import React from 'react';
|
||||
|
||||
function ErrorPage() {
|
||||
return <div>ErrorPage</div>;
|
||||
}
|
||||
|
||||
export default ErrorPage;
|
|
@ -1,14 +1,15 @@
|
|||
|
||||
import api from '@/api';
|
||||
import Notes from '@/components/Notes';
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
async function Page() {
|
||||
const data = await api.notes.getAllNotes();
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-4xl">Notes</h1>
|
||||
<Notes />
|
||||
<Notes data={data} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page
|
||||
export default Page;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
'use client';
|
||||
|
||||
function ErrorPage() {
|
||||
return <div>ErrorPage</div>;
|
||||
}
|
||||
|
||||
export default ErrorPage;
|
|
@ -1,8 +1,10 @@
|
|||
import api from '@/api';
|
||||
import UploadsList from '@/components/Lists/UploadsList';
|
||||
import React from 'react';
|
||||
|
||||
const Uploads = () => {
|
||||
return <UploadsList />;
|
||||
const Uploads = async () => {
|
||||
const uploads = await api.uploads.getAllUploads();
|
||||
return <UploadsList data={uploads}/>;
|
||||
};
|
||||
|
||||
export default Uploads;
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import api from '@/api'
|
||||
import ShortenUrlList from '@/components/Lists/ShortenUrlList'
|
||||
import React from 'react'
|
||||
|
||||
function Page() {
|
||||
async function Page() {
|
||||
const res = await api.url.getAllUrls()
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-4xl">URL Shortener</h1>
|
||||
<ShortenUrlList />
|
||||
<ShortenUrlList data={res} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
"use client"
|
||||
|
||||
import React from 'react'
|
||||
|
||||
function ErrorPage() {
|
||||
return (
|
||||
<div>ErrorPage</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ErrorPage
|
|
@ -1,61 +1,118 @@
|
|||
"use client"
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { ChangeEventHandler, useEffect, useState } from 'react';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Input from '@/components/ui/Input';
|
||||
import TagInput from '@/components/TagInput';
|
||||
import api from '@/api';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
function Page() {
|
||||
const [imageExts, setImageExts] = useState<string[]>([]);
|
||||
const [fileExts, setFileExts] = useState<string[]>([]);
|
||||
|
||||
const addImageExt = (tag: string) => {
|
||||
setImageExts(old => {
|
||||
return [...old, tag];
|
||||
});
|
||||
};
|
||||
const addFileExt = (tag: string) => {
|
||||
setFileExts(old => {
|
||||
return [...old, tag];
|
||||
});
|
||||
};
|
||||
|
||||
const [settings, setSettings] = useState<ISettings>({
|
||||
imageExtensions: [],
|
||||
fileExtensions: [],
|
||||
theme: '',
|
||||
language: '',
|
||||
});
|
||||
|
||||
const onChangeInput: ChangeEventHandler<
|
||||
HTMLInputElement | HTMLSelectElement
|
||||
> = evt => {
|
||||
const { name, value } = evt.target;
|
||||
setSettings(old => ({
|
||||
...old,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const addImageExt = (tag: string) => {
|
||||
setSettings(old => {
|
||||
return {
|
||||
...old,
|
||||
imageExtensions: [...old.imageExtensions, tag],
|
||||
};
|
||||
});
|
||||
};
|
||||
const addFileExt = (tag: string) => {
|
||||
setSettings(old => {
|
||||
return {
|
||||
...old,
|
||||
fileExtensions: [...old.fileExtensions, tag],
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const getSettings = async () => {
|
||||
const res = await api.settings.getCurrentSettings();
|
||||
setSettings(res);
|
||||
};
|
||||
|
||||
const updateSettings = async (key: string, value: string | string[]) => {
|
||||
try {
|
||||
await api.settings.updateASetting(key, value);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('error updating setting');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getSettings();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full mt-10">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="theme"
|
||||
className="block text-sm font-medium leading-6 text-white"
|
||||
<div className="flex items-center gap-4 w-full">
|
||||
<div className="w-full">
|
||||
<label
|
||||
htmlFor="theme"
|
||||
className="block text-sm font-medium leading-6 text-white"
|
||||
>
|
||||
Theme
|
||||
</label>
|
||||
<select
|
||||
id="theme"
|
||||
name="theme"
|
||||
className="mt-2 block w-full bg-transparent rounded-md py-1.5 pl-3 pr-10 text-white sm:text-sm sm:leading-6 border-primary border"
|
||||
value={settings.theme}
|
||||
onChange={onChangeInput}
|
||||
>
|
||||
<option value={'dark'}>Dark</option>
|
||||
</select>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => updateSettings('theme', settings.theme)}
|
||||
className="w-20 h-min mt-auto"
|
||||
>
|
||||
Theme
|
||||
</label>
|
||||
<select
|
||||
id="theme"
|
||||
name="theme"
|
||||
className="mt-2 block w-full bg-transparent rounded-md py-1.5 pl-3 pr-10 text-white sm:text-sm sm:leading-6 border-primary border"
|
||||
defaultValue="dark"
|
||||
>
|
||||
<option value={'dark'}>Dark</option>
|
||||
</select>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="Language"
|
||||
className="block text-sm font-medium leading-6 text-white"
|
||||
<div className="flex w-full items-center gap-4">
|
||||
<div className="w-full">
|
||||
<label
|
||||
htmlFor="Language"
|
||||
className="block text-sm font-medium leading-6 text-white"
|
||||
>
|
||||
Language
|
||||
</label>
|
||||
<select
|
||||
id="Language"
|
||||
name="language"
|
||||
className="mt-2 bg-transparent block w-full rounded-md py-1.5 pl-3 pr-10 text-white sm:text-sm sm:leading-6 border border-primary"
|
||||
value={settings.language}
|
||||
onChange={onChangeInput}
|
||||
>
|
||||
<option value={'en'}>English</option>
|
||||
</select>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => updateSettings('language', settings.language)}
|
||||
className="w-20 h-min mt-auto"
|
||||
>
|
||||
Language
|
||||
</label>
|
||||
<select
|
||||
id="Language"
|
||||
name="Language"
|
||||
className="mt-2 bg-transparent block w-full rounded-md py-1.5 pl-3 pr-10 text-white sm:text-sm sm:leading-6 border border-primary"
|
||||
defaultValue="en"
|
||||
>
|
||||
<option value={'en'}>English</option>
|
||||
</select>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center w-full">
|
||||
<div className="flex items-center w-full gap-4">
|
||||
<Input
|
||||
id="instance-url"
|
||||
withLabel
|
||||
|
@ -63,18 +120,39 @@ function Page() {
|
|||
type="text"
|
||||
className="w-full"
|
||||
/>
|
||||
<Button className="w-20 h-min ">Save</Button>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<TagInput
|
||||
tags={settings.imageExtensions}
|
||||
onAddTags={addImageExt}
|
||||
placeholder="Image Extensions"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() =>
|
||||
updateSettings('imageExtensions', settings.imageExtensions)
|
||||
}
|
||||
className="w-20 h-min "
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<TagInput
|
||||
tags={settings.fileExtensions}
|
||||
onAddTags={addFileExt}
|
||||
placeholder="File Extensions"
|
||||
/>
|
||||
<Button
|
||||
onClick={() =>
|
||||
updateSettings('fileExtensions', settings.fileExtensions)
|
||||
}
|
||||
className="w-20 h-min "
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
<TagInput
|
||||
tags={imageExts}
|
||||
onAddTags={addImageExt}
|
||||
placeholder="Image Extensions"
|
||||
/>
|
||||
<TagInput
|
||||
tags={fileExts}
|
||||
onAddTags={addFileExt}
|
||||
placeholder="File Extensions"
|
||||
/>
|
||||
<Button>Save</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import api from '@/api'
|
||||
import './globals.css'
|
||||
import { Source_Code_Pro } from 'next/font/google'
|
||||
|
||||
|
@ -14,9 +13,7 @@ export default function RootLayout({
|
|||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
api.setInstanceUrl("http://localhost:4000")
|
||||
api.setApiKey('SHX-uyblf-ixuiz');
|
||||
console.log(api.getAxios().defaults)
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${inter.className} bg-black text-white min-h-screen`}>{children}</body>
|
||||
|
|
|
@ -1,25 +1,11 @@
|
|||
import Button from "@/components/ui/Button";
|
||||
import {Github} from "lucide-react"
|
||||
import LoginForm from '@/components/LoginForm';
|
||||
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center text-center p-24 ">
|
||||
<div className="content max-w-3xl flex flex-col gap-2">
|
||||
<h1 className="text-5xl text-primary">SHX</h1>
|
||||
<p className="text-lg ">
|
||||
An platform platform meant to store and share files, images, text and
|
||||
URLs with ease
|
||||
</p>
|
||||
<p className="text-2xl my-8">Coming Soon...</p>
|
||||
<p className="text-lg">Find us on:</p>
|
||||
<div className="flex items-center justify-evenly">
|
||||
<a href="https://github.com/BRAVO68WEB/shx" target="_blank" >
|
||||
<Button size="icon" className="icon">
|
||||
<Github className="h-8 w-8" />
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<main className="w-screen h-screen flex items-center justify-center">
|
||||
<LoginForm />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import Button from '@/components/ui/Button';
|
||||
import { Github } from 'lucide-react';
|
||||
|
||||
export default function ComingSoon() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center text-center p-24 ">
|
||||
<div className="content max-w-3xl flex flex-col gap-2">
|
||||
<h1 className="text-5xl text-primary">SHX</h1>
|
||||
<p className="text-lg ">
|
||||
An platform platform meant to store and share files, images, text and
|
||||
URLs with ease
|
||||
</p>
|
||||
<p className="text-2xl my-8">Coming Soon...</p>
|
||||
<p className="text-lg">Find us on:</p>
|
||||
<div className="flex items-center justify-evenly">
|
||||
<a href="https://github.com/BRAVO68WEB/shx" target="_blank">
|
||||
<Button size="icon" className="icon">
|
||||
<Github className="h-8 w-8" />
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
|
@ -1,20 +1,25 @@
|
|||
'use client';
|
||||
|
||||
import api from '@/api';
|
||||
import Button from '@/components/ui/Button';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
const apiKeys = [
|
||||
{
|
||||
key: 'asdfasd****fsadf',
|
||||
enabled: true,
|
||||
lastUsed: 'asdfasdfasdf',
|
||||
},
|
||||
{
|
||||
key: 'disabled****fsadf',
|
||||
enabled: false,
|
||||
lastUsed: 'asdfasdfasdf',
|
||||
},
|
||||
];
|
||||
interface ApiKeyListProps {
|
||||
data: IApiKey[];
|
||||
}
|
||||
|
||||
function ApiKeyList() {
|
||||
function ApiKeyList({ data }: ApiKeyListProps) {
|
||||
const [apiKeys, setApiKeys] = useState(data);
|
||||
const onDisableApiKey = async (id: string) => {
|
||||
try {
|
||||
await api.apiKeys.disableApiKey(id);
|
||||
setApiKeys(old => old.filter(key => key.keyID !== id));
|
||||
} catch (err) {
|
||||
console.error('Error Deleting api key');
|
||||
toast.error('Error deleting api key');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<table className="min-w-full divide-y divide-gray-700">
|
||||
|
@ -32,34 +37,22 @@ function ApiKeyList() {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y p-2">
|
||||
{apiKeys.map(({ key, enabled,lastUsed }, index) => (
|
||||
<tr className="bg-gray-900 rounded" key={index}>
|
||||
{apiKeys.map(({ key, keyID }) => (
|
||||
<tr className="bg-gray-900 rounded" key={keyID}>
|
||||
<td className="whitespace-nowrap pl-4 text-sm font-medium text-white">
|
||||
<p className='text-xl'>{key}</p>
|
||||
<p className='text-xs text-gray-400'>Last Used: {lastUsed}</p>
|
||||
<p className="text-xl">{key}</p>
|
||||
</td>
|
||||
<td className="relative whitespace-nowrap py-4 px-4 text-right text-sm font-medium icons flex center items-center gap-3">
|
||||
{enabled ? (
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Disable Api Key"
|
||||
title="Disable Api Key"
|
||||
className="rounded-full p-2 bg-red-100 text-red-600"
|
||||
>
|
||||
Disable
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Enable Api Key"
|
||||
title="Enable Api Key"
|
||||
className="rounded-full p-2 text-green-600 bg-green-100"
|
||||
>
|
||||
Enable
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Disable Api Key"
|
||||
title="Disable Api Key"
|
||||
className="rounded-full p-2 bg-red-100 text-red-600"
|
||||
onClick={() => onDisableApiKey(keyID)}
|
||||
>
|
||||
Disable
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
|
|
@ -2,39 +2,44 @@ import Button from '@/components/ui/Button';
|
|||
import { Edit, Trash2 } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
function NotesList() {
|
||||
interface NotesLitsProps {
|
||||
data: INote[];
|
||||
onDeleteNote: (note: string) => void;
|
||||
}
|
||||
|
||||
function NotesList({ data, onDeleteNote }: NotesLitsProps) {
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-1">
|
||||
<div className="bg-gray-900 p-5 flex w-full gap-2">
|
||||
<div className="text flex-1">
|
||||
<p className="w-full max-w-lg">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
|
||||
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||
aliquip ex ea commodo consequat.
|
||||
</p>
|
||||
</div>
|
||||
<div className="buttons flex h-min gap-3">
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Delete Image"
|
||||
title="Delete Image"
|
||||
className="rounded-full hover:bg-red-50 hover:text-red-600 text-red-300"
|
||||
>
|
||||
<Trash2 className="h-5 w-5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Edit Note"
|
||||
title="Edit Note"
|
||||
className="rounded-full hover:bg-black"
|
||||
>
|
||||
<Edit className="h-5 w-5 " />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{data.map(note => {
|
||||
return (
|
||||
<div key={note.gistID} className="bg-gray-900 p-5 flex w-full gap-2">
|
||||
<div className="text flex-1">
|
||||
<p className="w-full max-w-lg">{note.content}</p>
|
||||
</div>
|
||||
<div className="buttons flex h-min gap-3">
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Delete Note"
|
||||
title="Delete Note"
|
||||
className="rounded-full hover:bg-red-50 hover:text-red-600 text-red-300"
|
||||
onClick={() => onDeleteNote(note.gistID)}
|
||||
>
|
||||
<Trash2 className="h-5 w-5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Edit Note"
|
||||
title="Edit Note"
|
||||
className="rounded-full hover:bg-black"
|
||||
>
|
||||
<Edit className="h-5 w-5 " />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"use client"
|
||||
'use client';
|
||||
|
||||
import Modal from '@/components/Modal';
|
||||
import Button from '@/components/ui/Button';
|
||||
|
@ -6,10 +6,19 @@ import Input from '@/components/ui/Input';
|
|||
import { Plus, X } from 'lucide-react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
function ULRControls() {
|
||||
const [addModalOpen, setAddModalOpen] = useState(false);
|
||||
|
||||
interface URLContrlProps {
|
||||
onAddURL: (url: string) => Promise<void>;
|
||||
}
|
||||
|
||||
function URLControls({ onAddURL }: URLContrlProps) {
|
||||
const [addModalOpen, setAddModalOpen] = useState(false);
|
||||
const [input, setInput] = useState('');
|
||||
|
||||
const onClick = async () => {
|
||||
if (input.trim() === '') return;
|
||||
await onAddURL(input);
|
||||
setAddModalOpen(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-6 items-cetner w-full">
|
||||
|
@ -41,18 +50,18 @@ function ULRControls() {
|
|||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
id='url'
|
||||
name="url"
|
||||
withLabel={true}
|
||||
label='Original URL'
|
||||
id="url"
|
||||
name="url"
|
||||
withLabel={true}
|
||||
label="Original URL"
|
||||
value={input}
|
||||
onChange={evt => setInput(evt.target.value)}
|
||||
/>
|
||||
<Button onClick={() => setAddModalOpen(false)} >
|
||||
Add URl
|
||||
</Button>
|
||||
<Button onClick={onClick}>Add URl</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ULRControls;
|
||||
export default URLControls;
|
||||
|
|
|
@ -1,31 +1,76 @@
|
|||
"use client"
|
||||
'use client';
|
||||
|
||||
import React,{useState} from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Button from '@/components/ui/Button';
|
||||
import { Trash, Edit2, ArrowUpRight,X } from 'lucide-react';
|
||||
import ULRControls from './URLControls';
|
||||
import { Trash, Edit2, ArrowUpRight, X } from 'lucide-react';
|
||||
import URLControls from './URLControls';
|
||||
import Modal from '@/components/Modal';
|
||||
import Input from '@/components/ui/Input';
|
||||
import api from '@/api';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
const urls = [
|
||||
{
|
||||
id: 'afdas',
|
||||
originalURL: 'https://www.google.com',
|
||||
shortenedURL: 'https://www.google.com',
|
||||
},
|
||||
];
|
||||
interface ShortenUrlListProps {
|
||||
data: IUrl[];
|
||||
}
|
||||
|
||||
function ShortenUrlList() {
|
||||
const [editURLModal,setEditURLModal] = useState(false)
|
||||
interface EditUrlModal {
|
||||
state: boolean;
|
||||
id?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
function ShortenUrlList({ data }: ShortenUrlListProps) {
|
||||
const router = useRouter();
|
||||
const [editURLModal, setEditURLModal] = useState<EditUrlModal>({
|
||||
state: false,
|
||||
});
|
||||
const [input, setInput] = useState('');
|
||||
const [instanceUrl, setInstanceURL] = useState('');
|
||||
const onAddURL = async (url: string) => {
|
||||
try {
|
||||
await api.url.uploadUrl(url);
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error('Error adding url');
|
||||
}
|
||||
};
|
||||
const onEditUrl = async (url: string, id: string) => {
|
||||
if (!input.trim()) return;
|
||||
try {
|
||||
await api.url.editUrl({ original_url: url, id: id, short_key: input });
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error('Error updating url');
|
||||
} finally {
|
||||
setEditURLModal({ state: false });
|
||||
}
|
||||
};
|
||||
const onDeleteUrl = async (id: string) => {
|
||||
try {
|
||||
await api.url.deleteUrl(id);
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error('Error deleting url');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setInstanceURL(Cookies.get('instanceUrl') ?? '');
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<ULRControls />
|
||||
<URLControls onAddURL={onAddURL} />
|
||||
<table className="min-w-full divide-y divide-gray-700">
|
||||
<thead className="p-2">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="py-3.5 pl-4 pr-5 text-left text-lg font-semibold text-white"
|
||||
className="py-3.5 pl-4 pr-5 text-left text-lg font-semibold text-white"
|
||||
>
|
||||
Original URL
|
||||
</th>
|
||||
|
@ -41,14 +86,14 @@ function ShortenUrlList() {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y p-2">
|
||||
{urls.map(({ originalURL, shortenedURL, id }) => (
|
||||
<tr className="bg-gray-900 rounded" key={id}>
|
||||
<td className="whitespace-nowrap pl-4 pr-20 text-sm font-medium text-white">
|
||||
{data.map(({ original_url, urlID, short_key }) => (
|
||||
<tr className="bg-gray-900 rounded" key={urlID}>
|
||||
<td className="whitespace-nowrap pl-4 pr-20 truncate text-sm font-medium text-white">
|
||||
<div className="flex items-center gap-3">
|
||||
{originalURL}
|
||||
<p className="w-80 truncate">{original_url}</p>
|
||||
<a
|
||||
referrerPolicy="no-referrer"
|
||||
href={originalURL}
|
||||
href={original_url}
|
||||
target="_blank"
|
||||
className="p-2 bg-white bg-opacity-10 rounded cursor-pointer"
|
||||
>
|
||||
|
@ -58,10 +103,10 @@ function ShortenUrlList() {
|
|||
</td>
|
||||
<td className="whitespace-nowrap pl-4 text-sm font-medium text-white">
|
||||
<div className="flex items-center gap-3">
|
||||
{shortenedURL}
|
||||
<p className="w-80 truncate">{`${instanceUrl}/${short_key}`}</p>
|
||||
<a
|
||||
referrerPolicy="no-referrer"
|
||||
href={shortenedURL}
|
||||
href={`${instanceUrl}/${short_key}`}
|
||||
target="_blank"
|
||||
className="p-2 bg-white bg-opacity-10 rounded cursor-pointer"
|
||||
>
|
||||
|
@ -76,6 +121,7 @@ function ShortenUrlList() {
|
|||
aria-label="Delete URL"
|
||||
title="Delete URL"
|
||||
className="rounded-full p-2 bg-red-100 text-red-600"
|
||||
onClick={() => onDeleteUrl(urlID)}
|
||||
>
|
||||
<Trash className="h-4 w-4 " />
|
||||
</Button>
|
||||
|
@ -85,7 +131,13 @@ function ShortenUrlList() {
|
|||
aria-label="Edit URl Slug"
|
||||
title="Edit URL Slug"
|
||||
className="rounded-full p-2 hover:bg-black"
|
||||
onClick={() => setEditURLModal(true)}
|
||||
onClick={() =>
|
||||
setEditURLModal({
|
||||
state: true,
|
||||
id: urlID,
|
||||
url: original_url,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Edit2 className="h-4 w-4 " />
|
||||
</Button>
|
||||
|
@ -94,11 +146,14 @@ function ShortenUrlList() {
|
|||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<Modal open={editURLModal} onClose={() => setEditURLModal(false)}>
|
||||
<Modal
|
||||
open={editURLModal.state}
|
||||
onClose={() => setEditURLModal({ state: false })}
|
||||
>
|
||||
<div className="p-2">
|
||||
<div className="controls w-full flex items-center justify-end mb-4">
|
||||
<Button
|
||||
onClick={() => setEditURLModal(false)}
|
||||
onClick={() => setEditURLModal({ state: false })}
|
||||
size={'icon'}
|
||||
aria-label="Reset"
|
||||
title="Reset"
|
||||
|
@ -112,8 +167,16 @@ function ShortenUrlList() {
|
|||
name="new_slug"
|
||||
withLabel={true}
|
||||
label="New Slug"
|
||||
value={input}
|
||||
onChange={evt => setInput(evt.target.value)}
|
||||
/>
|
||||
<Button onClick={() => setEditURLModal(false)}>Modify URl</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
onEditUrl(editURLModal.url ?? '', editURLModal.id ?? '')
|
||||
}
|
||||
>
|
||||
Modify URl
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
|
|
|
@ -1,30 +1,23 @@
|
|||
import Button from '@/components/ui/Button';
|
||||
import { UploadsListComponentProps } from '@/types/list';
|
||||
import { ArrowUpRight, Trash } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
const files: UploadsListFile[] = [
|
||||
{
|
||||
name: 'fasdfasd fasd fas dfasa.png',
|
||||
date: '2023-05-29T08:08:07.289624+00:00',
|
||||
src: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072821_1280.jpg',
|
||||
},
|
||||
];
|
||||
|
||||
export default function GridList() {
|
||||
const GridList: React.FC<UploadsListComponentProps> = ({ data,onDelete }) => {
|
||||
return (
|
||||
<div className="w-full grid grid-cols-3 gap-2 p-4">
|
||||
{files.map((file, index) => (
|
||||
{data.map(file => (
|
||||
<div
|
||||
key={index}
|
||||
key={file.fileID}
|
||||
className="grid-item overflow-hidden rounded-md w-full aspect-square flex items-center justify-center bg-gray-900 relative group"
|
||||
>
|
||||
<img
|
||||
src={file.src}
|
||||
src={file.upload_url}
|
||||
alt=""
|
||||
className="w-full h-full object-contain object-center"
|
||||
/>
|
||||
<div className="flex items-center justify-between gap-5 w-full absolute bottom-0 left-0 right opacity-0 translate-y-full p-4 group-hover:opacity-100 group-hover:translate-y-0 transition-all">
|
||||
<p className='w-full truncate'>{file.name}</p>
|
||||
<p className="w-full truncate">{file.filename}</p>
|
||||
<div className="controls flex items-center gap-5">
|
||||
<Button
|
||||
variant="transparent"
|
||||
|
@ -32,22 +25,24 @@ export default function GridList() {
|
|||
aria-label="Delete Image"
|
||||
title="Delete Image"
|
||||
className="rounded-full p-2 hover:bg-red-50 hover:text-red-600 text-red-300"
|
||||
onClick={() => onDelete(file.fileID,file.deleteToken)}
|
||||
>
|
||||
<Trash className="h-4 w-4 " />
|
||||
</Button>
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Open Image"
|
||||
title="Open Image"
|
||||
<a
|
||||
href={file.upload_url}
|
||||
target="_blank"
|
||||
referrerPolicy="no-referrer"
|
||||
className="rounded-full p-2 hover:bg-black"
|
||||
>
|
||||
<ArrowUpRight className="h-4 w-4 " />
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default GridList;
|
||||
|
|
|
@ -2,35 +2,18 @@ import Button from '@/components/ui/Button';
|
|||
import { ArrowUpRight, Trash } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { UploadsListComponentProps } from '@/types/list';
|
||||
|
||||
const files: UploadsListFile[] = [
|
||||
{
|
||||
name: 'Lindsay Walton.png',
|
||||
date: '2023-05-29T08:08:07.289624+00:00',
|
||||
src: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072821_1280.jpg',
|
||||
},
|
||||
{
|
||||
name: 'Wlaton Lindsay.jpg',
|
||||
date: '2023-05-30T08:08:07.289624+00:00',
|
||||
src: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072821_1280.jpg',
|
||||
},
|
||||
];
|
||||
|
||||
interface LinearListProps{
|
||||
edit: boolean
|
||||
}
|
||||
|
||||
export default function LinearList({edit}:LinearListProps) {
|
||||
|
||||
|
||||
export default function LinearList({
|
||||
edit,
|
||||
data,
|
||||
onDelete,
|
||||
}: UploadsListComponentProps) {
|
||||
// parse a date from gmt format to iso format
|
||||
const parseDate = (date: string) => {
|
||||
return new Date(date).toISOString().split('T')[0];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-2 ">
|
||||
<table className="min-w-full divide-y divide-gray-700">
|
||||
|
@ -57,45 +40,48 @@ export default function LinearList({edit}:LinearListProps) {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y p-2">
|
||||
{files.map((person, index) => (
|
||||
<tr className="bg-gray-900 rounded" key={index}>
|
||||
<td className="relative whitespace-nowrap p-4 text-right text-sm font-medium">
|
||||
<input
|
||||
type="checkbox"
|
||||
className={cn(
|
||||
'h-4 w-4 rounded bg-transparent border-primary text-primary',
|
||||
{ hidden: !edit }
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
<td className="whitespace-nowrap pl-4 text-sm font-medium text-white">
|
||||
{person.name}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-9 py-4 text-sm text-gray-300">
|
||||
{parseDate(person.date)}
|
||||
</td>
|
||||
<td className="relative whitespace-nowrap py-4 px-4 text-right text-sm font-medium icons flex center items-center gap-3">
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Delete Image"
|
||||
title="Delete Image"
|
||||
className="rounded-full p-2 hover:bg-red-50 hover:text-red-600 text-red-300"
|
||||
>
|
||||
<Trash className="h-4 w-4 " />
|
||||
</Button>
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Open Image"
|
||||
title="Open Image"
|
||||
className="rounded-full p-2 hover:bg-black"
|
||||
>
|
||||
<ArrowUpRight className="h-4 w-4 " />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{data.map(
|
||||
({ fileID, filename, uploaded_at, upload_url, deleteToken }) => (
|
||||
<tr className="bg-gray-900 rounded" key={fileID}>
|
||||
<td className="relative whitespace-nowrap p-4 text-right text-sm font-medium">
|
||||
<input
|
||||
type="checkbox"
|
||||
className={cn(
|
||||
'h-4 w-4 rounded bg-transparent border-primary text-primary',
|
||||
{ hidden: !edit }
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
<td className="whitespace-nowrap pl-4 text-sm font-medium text-white">
|
||||
{filename}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-9 py-4 text-sm text-gray-300">
|
||||
{parseDate(uploaded_at)}
|
||||
</td>
|
||||
<td className="relative whitespace-nowrap py-4 px-4 text-right text-sm font-medium icons flex center items-center gap-3">
|
||||
<Button
|
||||
variant="transparent"
|
||||
size={'icon'}
|
||||
aria-label="Delete Image"
|
||||
title="Delete Image"
|
||||
className="rounded-full p-2 hover:bg-red-50 hover:text-red-600 text-red-300"
|
||||
onClick={() => onDelete(fileID, deleteToken)}
|
||||
>
|
||||
<Trash className="h-4 w-4 " />
|
||||
</Button>
|
||||
<a
|
||||
href={upload_url}
|
||||
target="_blank"
|
||||
download={false}
|
||||
referrerPolicy="no-referrer"
|
||||
className="rounded-full p-2 hover:bg-black"
|
||||
>
|
||||
<ArrowUpRight className="h-4 w-4 " />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,30 +1,53 @@
|
|||
import api from '@/api';
|
||||
import Modal from '@/components/Modal';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Input from "@/components/ui/Input"
|
||||
import Input from '@/components/ui/Input';
|
||||
import { Edit, Plus, X } from 'lucide-react';
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
interface UploadControlsProps{
|
||||
onEditClick: () => void;
|
||||
interface UploadControlsProps {
|
||||
onEditClick: () => void;
|
||||
onAddFile: (file: IFile) => void;
|
||||
onSearchInputChange: (input: string) => void;
|
||||
}
|
||||
|
||||
function UploadsControls({onEditClick}:UploadControlsProps) {
|
||||
|
||||
|
||||
function UploadsControls({
|
||||
onEditClick,
|
||||
onAddFile,
|
||||
onSearchInputChange,
|
||||
}: UploadControlsProps) {
|
||||
const [uploadModalOpen, setUploadModalOpen] = useState(false);
|
||||
const [fileUpload, setFileUpload] = useState<File | null>(null);
|
||||
|
||||
|
||||
const fileInputChange: React.ChangeEventHandler<HTMLInputElement> = evt => {
|
||||
const fileInputChange: React.ChangeEventHandler<
|
||||
HTMLInputElement
|
||||
> = async evt => {
|
||||
if (evt.target.value) {
|
||||
setFileUpload(evt.target.files ? evt.target.files[0] : null);
|
||||
try {
|
||||
const file = evt.target.files ? evt.target.files[0] : null;
|
||||
setFileUpload(file);
|
||||
if (!file) return;
|
||||
const res = await api.uploads.uploadSingleFile({ file });
|
||||
onAddFile(res);
|
||||
} catch {
|
||||
toast.error('Error Uploading File');
|
||||
} finally {
|
||||
setFileUpload(null);
|
||||
setUploadModalOpen(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-6 items-cetner w-full">
|
||||
<Input id="search" name="search" placeholder="Search" />
|
||||
<Input
|
||||
id="search"
|
||||
name="search"
|
||||
placeholder="Search"
|
||||
onChange={evt => onSearchInputChange(evt.target.value)}
|
||||
/>
|
||||
|
||||
<div className="flex items-center">22/33</div>
|
||||
|
||||
|
@ -37,7 +60,9 @@ function UploadsControls({onEditClick}:UploadControlsProps) {
|
|||
<span>Add</span> <Plus />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {onEditClick()}}
|
||||
onClick={() => {
|
||||
onEditClick();
|
||||
}}
|
||||
className="my-2 flex justify-between items-center w-auto gap-2"
|
||||
>
|
||||
<span>Edit</span> <Edit />
|
||||
|
@ -59,7 +84,7 @@ function UploadsControls({onEditClick}:UploadControlsProps) {
|
|||
|
||||
<label
|
||||
htmlFor="fileUpload"
|
||||
className="input w-full h-20 border-primary border flex justify-center items-center"
|
||||
className="input w-full h-40 border-primary border flex justify-center items-center"
|
||||
>
|
||||
{fileUpload ? (
|
||||
<img
|
||||
|
@ -84,4 +109,4 @@ function UploadsControls({onEditClick}:UploadControlsProps) {
|
|||
);
|
||||
}
|
||||
|
||||
export default UploadsControls
|
||||
export default UploadsControls;
|
||||
|
|
|
@ -6,34 +6,68 @@ import Button from '@/components/ui/Button';
|
|||
import LinearList from './LinearList';
|
||||
import GridList from './GridList';
|
||||
import UploadsControls from './UploadsControls';
|
||||
import { UploadsListComponentProps } from '@/types/list';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import api from '@/api';
|
||||
import { useDebounce } from '@/hooks/useDebounce';
|
||||
|
||||
type ListOption = 0 | 1;
|
||||
|
||||
type ListOptions = {
|
||||
name: string;
|
||||
Icon: LucideIcon;
|
||||
List: React.FC;
|
||||
List: React.FC<UploadsListComponentProps>;
|
||||
}[];
|
||||
|
||||
const listOptions: ListOptions = [
|
||||
{ name: 'list', Icon: ListIcon, List: LinearList as React.FC<any> },
|
||||
{ name: 'list', Icon: ListIcon, List: LinearList },
|
||||
{ name: 'grid', Icon: GridIcon, List: GridList },
|
||||
];
|
||||
|
||||
const UploadsList = () => {
|
||||
interface UploadsListProps {
|
||||
data: IFile[];
|
||||
}
|
||||
|
||||
const UploadsList: React.FC<UploadsListProps> = ({ data }) => {
|
||||
const [listOption, setListOption] = useState<ListOption>(0);
|
||||
const [files, setFiles] = useState<IFile[]>(data);
|
||||
const [edit, setEdit] = useState<boolean>(false);
|
||||
|
||||
const searchFiles = useDebounce(async (search: string) => {
|
||||
const res = await api.uploads.getAllUploads(search);
|
||||
setFiles(res);
|
||||
}, 500);
|
||||
|
||||
const toggleEdit = () => setEdit(!edit);
|
||||
|
||||
const ListComponent:React.FC<any> = listOptions[listOption].List
|
||||
|
||||
function changeListOption(option: ListOption){
|
||||
return () => setListOption(option)
|
||||
const ListComponent = listOptions[listOption].List;
|
||||
|
||||
function changeListOption(option: ListOption) {
|
||||
return () => setListOption(option);
|
||||
}
|
||||
|
||||
const onAddFile = (file: IFile) => {
|
||||
setFiles([file, ...files]);
|
||||
};
|
||||
|
||||
const onDelete = async (fileID: string, deleteToken: string) => {
|
||||
try {
|
||||
await api.uploads.deleteSingleFile({ fileID, deleteToken });
|
||||
setFiles(old => {
|
||||
return old.filter(file => file.fileID !== fileID);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error('Error deleting file');
|
||||
}
|
||||
};
|
||||
|
||||
const onSearchChange = (input: string) => {
|
||||
searchFiles(input);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='p-5'>
|
||||
<div className="p-5">
|
||||
<div className="toolbar flex py-2 bg-primary items-center justify-center gap-5 mb-10 rounded-md">
|
||||
{listOptions.map(({ name, Icon }, index) => {
|
||||
return (
|
||||
|
@ -50,8 +84,12 @@ const UploadsList = () => {
|
|||
);
|
||||
})}
|
||||
</div>
|
||||
<UploadsControls onEditClick={toggleEdit} />
|
||||
<ListComponent edit={edit}/>
|
||||
<UploadsControls
|
||||
onEditClick={toggleEdit}
|
||||
onAddFile={onAddFile}
|
||||
onSearchInputChange={onSearchChange}
|
||||
/>
|
||||
<ListComponent edit={edit} data={files} onDelete={onDelete} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,28 +1,66 @@
|
|||
'use client';
|
||||
|
||||
import React, { FormEventHandler } from 'react';
|
||||
import React from 'react';
|
||||
import Button from './ui/Button';
|
||||
import Input from './ui/Input';
|
||||
import Cookies from 'js-cookie';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { LoginSchema, LoginType } from '@/lib/validators/login';
|
||||
import api from '@/api';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const LoginForm = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const onSubmit:FormEventHandler<HTMLFormElement> = (evt) => {
|
||||
evt.preventDefault();
|
||||
}
|
||||
const { register, handleSubmit } = useForm<LoginType>({
|
||||
resolver: zodResolver(LoginSchema),
|
||||
});
|
||||
|
||||
const onSubmit = async ({ masterkey, instanceUrl }: LoginType) => {
|
||||
Cookies.set('masterKey', masterkey);
|
||||
Cookies.set('instanceUrl', instanceUrl);
|
||||
try {
|
||||
const res = await api.apiKeys.createKey();
|
||||
if (res) {
|
||||
Cookies.set('apiKey', res);
|
||||
router.push('/dashboard');
|
||||
} else {
|
||||
Cookies.remove('masterKey');
|
||||
Cookies.remove('instanceUrl');
|
||||
Cookies.remove('apiKey');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('error');
|
||||
Cookies.remove('masterKey');
|
||||
Cookies.remove('instanceUrl');
|
||||
Cookies.remove('apiKey');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={onSubmit}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="w-full max-w-2xl text-primary border border-primary rounded-lg p-10"
|
||||
>
|
||||
<Input
|
||||
{...register('masterkey')}
|
||||
label="Master Key"
|
||||
withLabel={true}
|
||||
placeholder="Master Key"
|
||||
type="text"
|
||||
name="masterKey"
|
||||
id="masterKey"
|
||||
/>
|
||||
<Input
|
||||
{...register('instanceUrl')}
|
||||
label="Instance Url"
|
||||
withLabel={true}
|
||||
placeholder="Instance Url"
|
||||
type="text"
|
||||
id="instanceUrl"
|
||||
/>
|
||||
<Button type="submit" className="mt-8">
|
||||
Sign in
|
||||
</Button>
|
||||
|
|
|
@ -1,64 +1,95 @@
|
|||
'use client';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Input from '@/components/ui/Input';
|
||||
import { PlusIcon, SearchIcon } from 'lucide-react';
|
||||
import { PlusIcon } from 'lucide-react';
|
||||
import NotesList from './Lists/NotesList';
|
||||
import TextArea from './ui/TextArea';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { AddNoteType, addNoteSchema } from '@/lib/validators/notes';
|
||||
import { cn } from '@/lib/utils';
|
||||
import api from '@/api';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useDebounce } from '@/hooks/useDebounce';
|
||||
import Modal from './Modal';
|
||||
|
||||
function Notes() {
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
const [dialogOpen,setDialogOpen] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
} = useForm<AddNoteType>({
|
||||
interface NotesProps {
|
||||
data: INote[];
|
||||
}
|
||||
|
||||
function Notes({ data }: NotesProps) {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [notes, setNotes] = useState<INote[]>(data);
|
||||
const { register, handleSubmit } = useForm<AddNoteType>({
|
||||
resolver: zodResolver(addNoteSchema),
|
||||
});
|
||||
const addNoteSubmit = (data: AddNoteType) => {
|
||||
console.log(data)
|
||||
const search = useDebounce(async (input: string) => {
|
||||
try {
|
||||
const res = await api.notes.getAllNotes(input);
|
||||
setNotes(res);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('A error occurred while searching');
|
||||
}
|
||||
}, 500);
|
||||
const addNoteSubmit = async (data: AddNoteType) => {
|
||||
try {
|
||||
const res = await api.notes.uploadSingleNote(data);
|
||||
setNotes(old => [res, ...old]);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Error Uploading Data');
|
||||
} finally {
|
||||
setDialogOpen(false);
|
||||
}
|
||||
};
|
||||
const onDeleteNote = async (noteID: string) => {
|
||||
try {
|
||||
api.notes.deleteSingleNote({ noteID });
|
||||
setNotes(old => old.filter(note => note.gistID !== noteID));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Error Deleting Note');
|
||||
}
|
||||
};
|
||||
const openAddNoteDialog = () => {
|
||||
setDialogOpen(true)
|
||||
};
|
||||
const closeAddNoteDialog = () => {
|
||||
setDialogOpen(false)
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-gray-900 p-5 flex items-center w-full gap-2 my-10">
|
||||
<Input type={'text'} placeholder="Search Notes" className="flex-1" />
|
||||
<Button size="icon">
|
||||
<Input
|
||||
onChange={evt => search(evt.target.value)}
|
||||
type={'text'}
|
||||
placeholder="Search Notes"
|
||||
className="flex-1"
|
||||
/>
|
||||
{/* <Button size="icon">
|
||||
<SearchIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</Button> */}
|
||||
<Button onClick={openAddNoteDialog} title="Add a note" size="icon">
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<NotesList />
|
||||
<dialog
|
||||
className={cn("fixed top-0 left-0 w-screen h-screen flex justify-center items-center bg-transparent",!dialogOpen?"hidden":"")}
|
||||
ref={dialogRef}
|
||||
onClick={closeAddNoteDialog}
|
||||
>
|
||||
<NotesList data={notes} onDeleteNote={onDeleteNote} />
|
||||
<Modal open={dialogOpen} onClose={() => setDialogOpen(false)}>
|
||||
<form
|
||||
onClick={(e) => {e.stopPropagation();}}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onSubmit={handleSubmit(addNoteSubmit)}
|
||||
className="w-full max-w-xl bg-gray-800 text-primary p-5 rounded flex flex-col gap-2"
|
||||
>
|
||||
<TextArea
|
||||
{...register('text')}
|
||||
{...register('content')}
|
||||
placeholder="Write your text ..."
|
||||
withLabel={true}
|
||||
label="Text"
|
||||
/>
|
||||
|
||||
<Input
|
||||
{...register('password')}
|
||||
{...register('passkey')}
|
||||
placeholder="Password"
|
||||
withLabel={true}
|
||||
label="Password"
|
||||
|
@ -68,25 +99,23 @@ function Notes() {
|
|||
<div className="relative flex items-start">
|
||||
<div className="flex h-6 items-center">
|
||||
<input
|
||||
{...register('burn')}
|
||||
{...register('isOneTimeOnly')}
|
||||
id="burn"
|
||||
type="checkbox"
|
||||
className="h-4 w-4 rounded border-primary bg-transparent text-primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm leading-6">
|
||||
<label htmlFor="burn" className="font-medium text-primary">
|
||||
Burn
|
||||
</label>
|
||||
<label htmlFor="burn" className="ml-3 text-sm leading-6">
|
||||
<p className="font-medium text-primary">Burn</p>
|
||||
<p id="offers-description" className="text-white">
|
||||
Burn the note after first use
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Button>Add Note</Button>
|
||||
</form>
|
||||
</dialog>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
export const useDebounce = (
|
||||
callback: (param: any) => void | Promise<void>,
|
||||
gap: number
|
||||
) => {
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
return (param: any) => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
callback(param);
|
||||
}, gap);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const LoginSchema = z.object({
|
||||
masterkey: z.string().max(256),
|
||||
instanceUrl: z.string().url(),
|
||||
});
|
||||
|
||||
export type LoginType = z.infer<typeof LoginSchema>;
|
|
@ -1,9 +1,9 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const addNoteSchema = z.object({
|
||||
text: z.string().min(1),
|
||||
password: z.string(),
|
||||
burn: z.boolean(),
|
||||
content: z.string().min(1),
|
||||
passkey: z.string().optional(),
|
||||
isOneTimeOnly: z.boolean(),
|
||||
});
|
||||
|
||||
export type AddNoteType = z.infer<typeof addNoteSchema>;
|
||||
|
|
|
@ -23,8 +23,9 @@
|
|||
"clsx": "^1.2.1",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.5",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.221.0",
|
||||
"next": "13.4.3",
|
||||
"next": "^13.4.7",
|
||||
"next-auth": "^4.22.1",
|
||||
"postcss": "8.4.24",
|
||||
"react": "18.2.0",
|
||||
|
@ -44,6 +45,7 @@
|
|||
"@storybook/nextjs": "^7.0.22",
|
||||
"@storybook/react": "^7.0.17",
|
||||
"@storybook/testing-library": "^0.0.14-next.2",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"eslint-plugin-storybook": "^0.6.12",
|
||||
"storybook": "^7.0.17"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
interface IApiKey {
|
||||
key: string;
|
||||
keyID: string;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface UploadsListComponentProps {
|
||||
edit: boolean;
|
||||
data: IFile[];
|
||||
onDelete:(fileID:string,deleteToken:string) => void;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
interface INote {
|
||||
gistID: string;
|
||||
content: string;
|
||||
gist_url_key: string;
|
||||
created_on: string;
|
||||
isPrivate: boolean;
|
||||
isOneTimeOnly: boolean;
|
||||
views: number;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
interface ISettings {
|
||||
theme: string;
|
||||
language: string;
|
||||
imageExtensions: string[];
|
||||
fileExtensions: string[];
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
interface IUrl {
|
||||
clicks: number;
|
||||
original_url: string;
|
||||
short_key: string;
|
||||
urlID: string;
|
||||
}
|
132
yarn.lock
132
yarn.lock
|
@ -2290,6 +2290,18 @@
|
|||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4":
|
||||
<<<<<<< HEAD
|
||||
version "7.22.3"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz"
|
||||
integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9":
|
||||
version "7.21.9"
|
||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz"
|
||||
integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==
|
||||
=======
|
||||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec"
|
||||
integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==
|
||||
|
@ -2300,6 +2312,7 @@
|
|||
version "7.22.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
|
||||
integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==
|
||||
>>>>>>> dev
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.22.5"
|
||||
"@babel/parser" "^7.22.5"
|
||||
|
@ -3434,10 +3447,17 @@
|
|||
pump "^3.0.0"
|
||||
tar-fs "^2.1.1"
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@next/env@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.7.tgz#ca12d341edb128ca70384635bd2794125ffb1c01"
|
||||
integrity sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw==
|
||||
=======
|
||||
"@next/env@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.3.tgz#cb00bdd43a0619a79a52c9336df8a0aa84f8f4bf"
|
||||
integrity sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ==
|
||||
>>>>>>> dev
|
||||
|
||||
"@next/eslint-plugin-next@13.4.5":
|
||||
version "13.4.5"
|
||||
|
@ -3446,26 +3466,37 @@
|
|||
dependencies:
|
||||
glob "7.1.7"
|
||||
|
||||
"@next/swc-darwin-arm64@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.3.tgz#2d6c99dd5afbcce37e4ba0f64196317a1259034d"
|
||||
integrity sha512-yx18udH/ZmR4Bw4M6lIIPE3JxsAZwo04iaucEfA2GMt1unXr2iodHUX/LAKNyi6xoLP2ghi0E+Xi1f4Qb8f1LQ==
|
||||
"@next/swc-darwin-arm64@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.7.tgz#5e36c26dda5b0bc0ea15d8555d0abd71a1ef4b5d"
|
||||
integrity sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==
|
||||
|
||||
"@next/swc-darwin-x64@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.3.tgz#162b15fb8a54d9f64e69c898ebeb55b7dac9bddd"
|
||||
integrity sha512-Mi8xJWh2IOjryAM1mx18vwmal9eokJ2njY4nDh04scy37F0LEGJ/diL6JL6kTXi0UfUCGbMsOItf7vpReNiD2A==
|
||||
"@next/swc-darwin-x64@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.7.tgz#4c14ec14b200373cd602589086cb1253a28cd803"
|
||||
integrity sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.3.tgz#aee57422f11183d6a2e4a2e8aa23b9285873e18f"
|
||||
integrity sha512-aBvtry4bxJ1xwKZ/LVPeBGBwWVwxa4bTnNkRRw6YffJnn/f4Tv4EGDPaVeYHZGQVA56wsGbtA6nZMuWs/EIk4Q==
|
||||
"@next/swc-linux-arm64-gnu@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.7.tgz#e7819167ec876ddac5a959e4c7bce4d001f0e924"
|
||||
integrity sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==
|
||||
|
||||
"@next/swc-linux-arm64-musl@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.3.tgz#c10b6aaaa47b341c6c9ea15f8b0ddb37e255d035"
|
||||
integrity sha512-krT+2G3kEsEUvZoYte3/2IscscDraYPc2B+fDJFipPktJmrv088Pei/RjrhWm5TMIy5URYjZUoDZdh5k940Dyw==
|
||||
"@next/swc-linux-arm64-musl@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.7.tgz#0cac0f01d4e308b439e6c33182bed77835fe383b"
|
||||
integrity sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@next/swc-linux-x64-gnu@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.7.tgz#feb61e16a68c67f3ef230f30d9562a3783c7bd59"
|
||||
integrity sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==
|
||||
|
||||
"@next/swc-linux-x64-musl@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.7.tgz#02179ecfa6d24a2956c2b54f7d27a050568bbf24"
|
||||
integrity sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==
|
||||
=======
|
||||
"@next/swc-linux-x64-gnu@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.3.tgz#3f85bc5591c6a0d4908404f7e88e3c04f4462039"
|
||||
|
@ -3475,21 +3506,22 @@
|
|||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.3.tgz#f4535adc2374a86bc8e43af149b551567df065de"
|
||||
integrity sha512-jySgSXE48shaLtcQbiFO9ajE9mqz7pcAVLnVLvRIlUHyQYR/WyZdK8ehLs65Mz6j9cLrJM+YdmdJPyV4WDaz2g==
|
||||
>>>>>>> dev
|
||||
|
||||
"@next/swc-win32-arm64-msvc@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.3.tgz#e76106d85391c308c5ed70cda2bca2c582d65536"
|
||||
integrity sha512-5DxHo8uYcaADiE9pHrg8o28VMt/1kR8voDehmfs9AqS0qSClxAAl+CchjdboUvbCjdNWL1MISCvEfKY2InJ3JA==
|
||||
"@next/swc-win32-arm64-msvc@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.7.tgz#274b7f00a2ec5934af73db15da8459e8647bfaed"
|
||||
integrity sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==
|
||||
|
||||
"@next/swc-win32-ia32-msvc@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.3.tgz#8eb5d9dd71ed7a971671291605ad64ad522fb3bc"
|
||||
integrity sha512-LaqkF3d+GXRA5X6zrUjQUrXm2MN/3E2arXBtn5C7avBCNYfm9G3Xc646AmmmpN3DJZVaMYliMyCIQCMDEzk80w==
|
||||
"@next/swc-win32-ia32-msvc@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.7.tgz#4a95c106a6db2eee3a4c1352b77995e298d7446a"
|
||||
integrity sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==
|
||||
|
||||
"@next/swc-win32-x64-msvc@13.4.3":
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.3.tgz#c7b2b1b9e158fd7749f8209e68ee8e43a997eb4c"
|
||||
integrity sha512-jglUk/x7ZWeOJWlVoKyIAkHLTI+qEkOriOOV+3hr1GyiywzcqfI7TpFSiwC7kk1scOiH7NTFKp8mA3XPNO9bDw==
|
||||
"@next/swc-win32-x64-msvc@13.4.7":
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.7.tgz#5137780f58d7f0230adc293a0429821bfa7d8c21"
|
||||
integrity sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
|
@ -5067,6 +5099,11 @@
|
|||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/js-cookie@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e"
|
||||
integrity sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==
|
||||
|
||||
"@types/js-yaml@^4.0.0":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
|
||||
|
@ -5206,6 +5243,9 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@types/react@*", "@types/react@18.2.7", "@types/react@>=16":
|
||||
=======
|
||||
"@types/react@*", "@types/react@>=16":
|
||||
version "18.2.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.13.tgz#a98c09bde8b18f80021935b11d2d29ef5f4dcb2f"
|
||||
|
@ -5216,6 +5256,7 @@
|
|||
csstype "^3.0.2"
|
||||
|
||||
"@types/react@18.2.7":
|
||||
>>>>>>> dev
|
||||
version "18.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.7.tgz#dfb4518042a3117a045b8c222316f83414a783b3"
|
||||
integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==
|
||||
|
@ -10255,6 +10296,11 @@ jpeg-js@^0.4.4:
|
|||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
|
||||
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
|
||||
|
||||
js-cookie@^3.0.5:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
|
||||
integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
@ -11174,28 +11220,36 @@ next-auth@^4.22.1:
|
|||
preact-render-to-string "^5.1.19"
|
||||
uuid "^8.3.2"
|
||||
|
||||
<<<<<<< HEAD
|
||||
next@^13.4.7:
|
||||
version "13.4.7"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-13.4.7.tgz#2ab20e6fada2e25cb81bd17f68956705ffd9824e"
|
||||
integrity sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==
|
||||
=======
|
||||
next@13.4.3:
|
||||
version "13.4.3"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-13.4.3.tgz#7f417dec9fa2731d8c1d1819a1c7d0919ad6fc75"
|
||||
integrity sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA==
|
||||
>>>>>>> dev
|
||||
dependencies:
|
||||
"@next/env" "13.4.3"
|
||||
"@next/env" "13.4.7"
|
||||
"@swc/helpers" "0.5.1"
|
||||
busboy "1.6.0"
|
||||
caniuse-lite "^1.0.30001406"
|
||||
postcss "8.4.14"
|
||||
styled-jsx "5.1.1"
|
||||
watchpack "2.4.0"
|
||||
zod "3.21.4"
|
||||
optionalDependencies:
|
||||
"@next/swc-darwin-arm64" "13.4.3"
|
||||
"@next/swc-darwin-x64" "13.4.3"
|
||||
"@next/swc-linux-arm64-gnu" "13.4.3"
|
||||
"@next/swc-linux-arm64-musl" "13.4.3"
|
||||
"@next/swc-linux-x64-gnu" "13.4.3"
|
||||
"@next/swc-linux-x64-musl" "13.4.3"
|
||||
"@next/swc-win32-arm64-msvc" "13.4.3"
|
||||
"@next/swc-win32-ia32-msvc" "13.4.3"
|
||||
"@next/swc-win32-x64-msvc" "13.4.3"
|
||||
"@next/swc-darwin-arm64" "13.4.7"
|
||||
"@next/swc-darwin-x64" "13.4.7"
|
||||
"@next/swc-linux-arm64-gnu" "13.4.7"
|
||||
"@next/swc-linux-arm64-musl" "13.4.7"
|
||||
"@next/swc-linux-x64-gnu" "13.4.7"
|
||||
"@next/swc-linux-x64-musl" "13.4.7"
|
||||
"@next/swc-win32-arm64-msvc" "13.4.7"
|
||||
"@next/swc-win32-ia32-msvc" "13.4.7"
|
||||
"@next/swc-win32-x64-msvc" "13.4.7"
|
||||
|
||||
nextgen-events@^1.3.4:
|
||||
version "1.5.3"
|
||||
|
@ -14402,7 +14456,7 @@ walker@^1.0.8:
|
|||
dependencies:
|
||||
makeerror "1.0.12"
|
||||
|
||||
watchpack@^2.2.0, watchpack@^2.4.0:
|
||||
watchpack@2.4.0, watchpack@^2.2.0, watchpack@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
|
||||
|
|
Loading…
Reference in New Issue