Make registry more responsive
Add dynamic route for clickable tiles Make copy url notification instead of alert Add achitecture to details
This commit is contained in:
parent
e7c1744d83
commit
c2c1599221
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,6 +4,8 @@ dist
|
|||||||
target
|
target
|
||||||
packages/next/wasm/@next
|
packages/next/wasm/@next
|
||||||
out
|
out
|
||||||
|
site/public/icons
|
||||||
|
site/public/list.json
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
function App({ Component, pageProps, app }) {
|
function App({ Component, pageProps, app }) {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const viewexample = (app) => {
|
||||||
|
router.push({
|
||||||
|
pathname: '/addapp/[app]',
|
||||||
|
query: { app: btoa(app.name)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-[245px] h-[88px] transition-all relative cursor-pointer group flex p-2 items-center justify-center bg-slate-100/90 dark:bg-slate-900/90 shadow rounded hover:shadow-xl hover:bg-gradient-to-r hover:from-slate-900 hover:to-cyan-800 hover:text-white">
|
<div onClick={() => viewexample(app)} className="w-[245px] h-[88px] transition-all relative cursor-pointer group flex p-2 items-center justify-center bg-slate-100/90 dark:bg-slate-900/90 shadow rounded hover:shadow-xl hover:bg-gradient-to-r hover:from-slate-900 hover:to-cyan-800 hover:text-white">
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<div className="show-grid flex h-full items-center">
|
<div className="show-grid flex h-full items-center">
|
||||||
<div className="kasmcard-img flex h-full mx-4 items-center justify-center">
|
<div className="kasmcard-img flex h-full mx-4 items-center justify-center">
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import Bubbles from '../components/Bubbles'
|
import Bubbles from '../components/Bubbles'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { NotificationManager } from 'react-notifications';
|
||||||
|
|
||||||
export default function Header({ searchText, changeSearch }) {
|
export default function Header({ searchText, changeSearch }) {
|
||||||
|
|
||||||
@ -11,13 +12,13 @@ export default function Header({ searchText, changeSearch }) {
|
|||||||
textField.select()
|
textField.select()
|
||||||
document.execCommand('copy')
|
document.execCommand('copy')
|
||||||
textField.remove()
|
textField.remove()
|
||||||
alert('URL copied to clipboard')
|
NotificationManager.info('URL successfully copied to clipboard', 'Copy URL', 4000);
|
||||||
}
|
}
|
||||||
const listUrl = process.env.listUrl;
|
const listUrl = process.env.listUrl;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="relative font-light overflow-hidden bg-gradient-to-tr from-slate-900 to-cyan-800 p-32 py-8 text-white flex justify-between items-center">
|
<header className="relative font-light overflow-hidden bg-gradient-to-tr from-slate-900 to-cyan-800 p-8 xl:px-32 text-white gap-5 md:gap-0 flex flex-wrap justify-center items-center">
|
||||||
<Bubbles />
|
<Bubbles />
|
||||||
<div className='relative z-10'>
|
<div className='relative z-10'>
|
||||||
<div className="text-3xl">{process.env.name}</div>
|
<div className="text-3xl">{process.env.name}</div>
|
||||||
@ -45,8 +46,8 @@ export default function Header({ searchText, changeSearch }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav className='relative z-10 mx-12'>
|
<nav className='relative z-10 mx-12'>
|
||||||
<Link href="/" className={'p-4 rounded-full border border-solid' + (router.pathname == "/" ? ' border-white/30' : ' border-transparent')}>Library</Link>
|
<Link href="/" className={'p-4 inline-block rounded-full border border-solid' + (router.pathname == "/" ? ' border-white/30' : ' border-transparent')}>Library</Link>
|
||||||
<Link href="/addapp" className={'p-4 rounded-full border border-solid' + (router.pathname == "/addapp" ? ' bg-black/10 border-white/30' : ' border-transparent')}>Add App</Link>
|
<Link href="/addapp" className={'p-4 inline-block rounded-full border border-solid' + (router.pathname == "/addapp" ? ' bg-black/10 border-white/30' : ' border-transparent')}>Add App</Link>
|
||||||
</nav>
|
</nav>
|
||||||
<div className="grow flex justify-center relative z-10">
|
<div className="grow flex justify-center relative z-10">
|
||||||
<div className='bg-black/10 shadow border border-1 border-white/30 rounded flex w-full max-w-md'>
|
<div className='bg-black/10 shadow border border-1 border-white/30 rounded flex w-full max-w-md'>
|
||||||
@ -62,7 +63,7 @@ export default function Header({ searchText, changeSearch }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button className='p-4 relative z-10 px-5 bg-emerald-600 m-2 rounded items-center text-white/70 flex cursor-pointer' onClick={() => { copyToClipboard() }}>
|
<button className='p-4 relative z-10 px-5 bg-cyan-700 border-t border-white/20 border-solid hover:bg-slate-900 transition shadow-lg m-2 rounded items-center text-white/70 flex cursor-pointer' onClick={() => { copyToClipboard() }}>
|
||||||
<span className="mr-3">App Registry Link</span>
|
<span className="mr-3">App Registry Link</span>
|
||||||
<svg style={{ height: '14px', fill: '#fff' }} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M224 0c-35.3 0-64 28.7-64 64V288c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H224zM64 160c-35.3 0-64 28.7-64 64V448c0 35.3 28.7 64 64 64H288c35.3 0 64-28.7 64-64V384H288v64H64V224h64V160H64z" /></svg>
|
<svg style={{ height: '14px', fill: '#fff' }} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M224 0c-35.3 0-64 28.7-64 64V288c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H224zM64 160c-35.3 0-64 28.7-64 64V448c0 35.3 28.7 64 64 64H288c35.3 0 64-28.7 64-64V384H288v64H64V224h64V160H64z" /></svg>
|
||||||
</button>
|
</button>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import Header from './header'
|
import Header from './header'
|
||||||
import Footer from './footer'
|
import Footer from './footer'
|
||||||
|
import 'react-notifications/lib/notifications.css';
|
||||||
|
import { NotificationContainer } from 'react-notifications';
|
||||||
|
|
||||||
export default function Layout({ children, searchText, changeSearch }) {
|
export default function Layout({ children, searchText, changeSearch }) {
|
||||||
return (
|
return (
|
||||||
@ -9,6 +11,7 @@ export default function Layout({ children, searchText, changeSearch }) {
|
|||||||
<Header searchText={searchText} changeSearch={changeSearch} />
|
<Header searchText={searchText} changeSearch={changeSearch} />
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
<NotificationContainer/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
76
site/package-lock.json
generated
76
site/package-lock.json
generated
@ -5,6 +5,7 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
|
"name": "site",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
@ -12,6 +13,7 @@
|
|||||||
"next": "13.0.0",
|
"next": "13.0.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-notifications": "^1.7.4",
|
||||||
"react-select": "^5.6.1"
|
"react-select": "^5.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -86,9 +88,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/core/node_modules/json5": {
|
"node_modules/@babel/core/node_modules/json5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"json5": "lib/cli.js"
|
"json5": "lib/cli.js"
|
||||||
@ -1532,6 +1534,11 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||||
|
},
|
||||||
"node_modules/client-only": {
|
"node_modules/client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
@ -3015,9 +3022,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
@ -3801,6 +3808,28 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-notifications": {
|
||||||
|
"version": "1.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-notifications/-/react-notifications-1.7.4.tgz",
|
||||||
|
"integrity": "sha512-dsR7mUQfe8YdFLqVsjT0GFd4n26UWkzefdjMELfEVygjuuyU6ZZ0LpZhFHdfmraGeBFLWHNxygpGlHHituUyjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": "6.4.1",
|
||||||
|
"classnames": "^2.1.1",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react-transition-group": "^4.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-notifications/node_modules/acorn": {
|
||||||
|
"version": "6.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||||
|
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-select": {
|
"node_modules/react-select": {
|
||||||
"version": "5.6.1",
|
"version": "5.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.6.1.tgz",
|
||||||
@ -4571,9 +4600,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
@ -5563,6 +5592,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"classnames": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||||
|
},
|
||||||
"client-only": {
|
"client-only": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
@ -6676,9 +6710,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
@ -7203,6 +7237,24 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"react-notifications": {
|
||||||
|
"version": "1.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-notifications/-/react-notifications-1.7.4.tgz",
|
||||||
|
"integrity": "sha512-dsR7mUQfe8YdFLqVsjT0GFd4n26UWkzefdjMELfEVygjuuyU6ZZ0LpZhFHdfmraGeBFLWHNxygpGlHHituUyjQ==",
|
||||||
|
"requires": {
|
||||||
|
"acorn": "6.4.1",
|
||||||
|
"classnames": "^2.1.1",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react-transition-group": "^4.4.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "6.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||||
|
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-select": {
|
"react-select": {
|
||||||
"version": "5.6.1",
|
"version": "5.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.6.1.tgz",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"next": "13.0.0",
|
"next": "13.0.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-notifications": "^1.7.4",
|
||||||
"react-select": "^5.6.1"
|
"react-select": "^5.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import CreatableSelect from 'react-select/creatable';
|
import CreatableSelect from 'react-select/creatable';
|
||||||
|
import Select from 'react-select';
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export default function AddApp() {
|
export default function AddApp() {
|
||||||
|
|
||||||
@ -27,6 +29,10 @@ export default function AddApp() {
|
|||||||
if (icon) {
|
if (icon) {
|
||||||
folder.file(application.image_src, icon.file)
|
folder.file(application.image_src, icon.file)
|
||||||
}
|
}
|
||||||
|
else if (inlineImage) {
|
||||||
|
const promise = fetch(inlineImage).then(response => response.blob())
|
||||||
|
folder.file(application.image_src, promise)
|
||||||
|
}
|
||||||
zip.generateAsync({ type: "blob" })
|
zip.generateAsync({ type: "blob" })
|
||||||
.then(function (content) {
|
.then(function (content) {
|
||||||
// Force down of the Zip file
|
// Force down of the Zip file
|
||||||
@ -34,36 +40,76 @@ export default function AddApp() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const name = useRef(null);
|
||||||
|
const friendly_name = useRef(null);
|
||||||
|
const description = useRef(null);
|
||||||
|
|
||||||
|
const [categories, setCategories] = useState(null)
|
||||||
|
const [architecture, setArchitecture] = useState(null)
|
||||||
const [icon, setIcon] = useState(null)
|
const [icon, setIcon] = useState(null)
|
||||||
const [ext, setExt] = useState('png')
|
const [ext, setExt] = useState('png')
|
||||||
|
const [inlineImage, setInlineImage] = useState(null)
|
||||||
|
|
||||||
const [application, setApplication] = useState({
|
const [application, setApplication] = useState({
|
||||||
friendly_name: null,
|
friendly_name: null,
|
||||||
image_src: null,
|
image_src: null,
|
||||||
description: null,
|
description: null,
|
||||||
name: null,
|
name: null,
|
||||||
cores: 2,
|
cores: 1,
|
||||||
memory: 2768,
|
memory: 1024,
|
||||||
gpu_count: 0,
|
gpu_count: 0,
|
||||||
cpu_allocation_method: "Inherit",
|
cpu_allocation_method: "Inherit",
|
||||||
docker_registry: "https://index.docker.io/v1/",
|
docker_registry: "https://index.docker.io/v1/",
|
||||||
volume_mappings: "{}",
|
|
||||||
run_config: "{}",
|
|
||||||
exec_config: "{}",
|
|
||||||
categories: [],
|
categories: [],
|
||||||
require_gpu: false,
|
require_gpu: false,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
restrict_to_network: false,
|
|
||||||
restrict_network_names: "[]",
|
|
||||||
allow_network_selection: false,
|
|
||||||
notes: null,
|
|
||||||
image_type: 'Container',
|
image_type: 'Container',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { app } = router.query
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('../../list.json')
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((apps) => {
|
||||||
|
if (app && app[0]) {
|
||||||
|
const appDetails = apps.apps.find(el => el.name === atob(app[0]))
|
||||||
|
delete appDetails['sha']
|
||||||
|
description.current.value = appDetails.description
|
||||||
|
name.current.value = appDetails.name
|
||||||
|
friendly_name.current.value = appDetails.friendly_name
|
||||||
|
if (appDetails.categories) {
|
||||||
|
let catMap = []
|
||||||
|
appDetails.categories.map((e) => catMap.push({
|
||||||
|
label: e,
|
||||||
|
value: e,
|
||||||
|
}))
|
||||||
|
setCategories(catMap)
|
||||||
|
}
|
||||||
|
if (appDetails.architecture) {
|
||||||
|
let archMap = []
|
||||||
|
appDetails.architecture.map((e) => archMap.push({
|
||||||
|
label: e,
|
||||||
|
value: e,
|
||||||
|
}))
|
||||||
|
setArchitecture(archMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
setInlineImage('../../icons/' + appDetails.image_src)
|
||||||
|
|
||||||
|
setApplication({
|
||||||
|
...application,
|
||||||
|
...appDetails
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [app])
|
||||||
|
|
||||||
const displayApplication = () => {
|
const displayApplication = () => {
|
||||||
return {
|
return {
|
||||||
...application,
|
...application,
|
||||||
categories: JSON.stringify(application.categories)
|
// categories: JSON.stringify(application.categories)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +144,26 @@ export default function AddApp() {
|
|||||||
}
|
}
|
||||||
updateapp.categories = items.map(cat => cat.value)
|
updateapp.categories = items.map(cat => cat.value)
|
||||||
setApplication(updateapp)
|
setApplication(updateapp)
|
||||||
|
let catMap = []
|
||||||
|
updateapp.categories.map((e) => catMap.push({
|
||||||
|
label: e,
|
||||||
|
value: e,
|
||||||
|
}))
|
||||||
|
setCategories(catMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateArchitecture = (items) => {
|
||||||
|
const updateapp = {
|
||||||
|
...application
|
||||||
|
}
|
||||||
|
updateapp.architecture = items.map(arch => arch.value)
|
||||||
|
setApplication(updateapp)
|
||||||
|
let archMap = []
|
||||||
|
updateapp.architecture.map((e) => archMap.push({
|
||||||
|
label: e,
|
||||||
|
value: e,
|
||||||
|
}))
|
||||||
|
setArchitecture(archMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -154,7 +220,7 @@ export default function AddApp() {
|
|||||||
<p className='mb-6 opacity-70'>Select the image to use, image will be renamed when it's downloaded.</p>
|
<p className='mb-6 opacity-70'>Select the image to use, image will be renamed when it's downloaded.</p>
|
||||||
|
|
||||||
<label className='mb-2 font-medium'>Friendly Name</label>
|
<label className='mb-2 font-medium'>Friendly Name</label>
|
||||||
<input name="friendly_name" onChange={handleChange} className='mb-2 p-2 rounded-lg bg-slate-100 border border-solid border-slate-400' />
|
<input ref={friendly_name} name="friendly_name" onChange={handleChange} className='mb-2 p-2 rounded-lg bg-slate-100 border border-solid border-slate-400' />
|
||||||
<p className='mb-6 opacity-70'>This is the name that will show for users</p>
|
<p className='mb-6 opacity-70'>This is the name that will show for users</p>
|
||||||
|
|
||||||
<label className='mb-2 font-medium'>Categories</label>
|
<label className='mb-2 font-medium'>Categories</label>
|
||||||
@ -164,23 +230,38 @@ export default function AddApp() {
|
|||||||
options={options}
|
options={options}
|
||||||
onChange={updateCategories}
|
onChange={updateCategories}
|
||||||
styles={customStyles}
|
styles={customStyles}
|
||||||
|
value={categories}
|
||||||
/>
|
/>
|
||||||
<p className='mb-6 mt-2 opacity-70'>You can select from the available option or create new ones.</p>
|
<p className='mb-6 mt-2 opacity-70'>You can select from the available option or create new ones.</p>
|
||||||
|
|
||||||
<label className='mb-2 font-medium'>Description</label>
|
<label className='mb-2 font-medium'>Description</label>
|
||||||
<input name="description" onChange={handleChange} className='mb-2 p-2 rounded-lg bg-slate-100 border border-solid border-slate-400' />
|
<input ref={description} name="description" onChange={handleChange} className='mb-2 p-2 rounded-lg bg-slate-100 border border-solid border-slate-400' />
|
||||||
<p className='mb-6 opacity-70'>A short description about the application</p>
|
<p className='mb-6 opacity-70'>A short description about the application</p>
|
||||||
|
|
||||||
<label className='mb-2 font-medium'>Docker Image</label>
|
<label className='mb-2 font-medium'>Docker Image</label>
|
||||||
<input name="name" onChange={handleChange} className='mb-2 p-2 rounded-lg bg-slate-100 border border-solid border-slate-400' />
|
<input ref={name} name="name" onChange={handleChange} className='mb-2 p-2 rounded-lg bg-slate-100 border border-solid border-slate-400' />
|
||||||
<p className='mb-6 opacity-70'>The docker image to use, i.e. <code className='text-xs p-1 px-2 rounded bg-white/40'>kasmweb/filezilla:develop</code></p>
|
<p className='mb-6 opacity-70'>The docker image to use, i.e. <code className='text-xs p-1 px-2 rounded bg-white/40'>kasmweb/filezilla:develop</code></p>
|
||||||
|
|
||||||
|
<label className='mb-2 font-medium'>Architecture</label>
|
||||||
|
<Select
|
||||||
|
name="architecture"
|
||||||
|
isMulti
|
||||||
|
options={[
|
||||||
|
{ value: 'amd64', label: 'amd64' },
|
||||||
|
{ value: 'arm64', label: 'arm64' },
|
||||||
|
]}
|
||||||
|
onChange={updateArchitecture}
|
||||||
|
styles={customStyles}
|
||||||
|
value={architecture}
|
||||||
|
/>
|
||||||
|
<p className='mb-6 mt-2 opacity-70'>You can select from the available option or create new ones.</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full lg:w-1/2 p-16 bg-slate-100'>
|
<div className='w-full lg:w-1/2 p-16 bg-slate-100'>
|
||||||
<App app={application} icon={icon} />
|
<App app={application} icon={icon} inlineImage={inlineImage} />
|
||||||
<pre className='my-8 overflow-y-auto text-xs'>{JSON.stringify(displayApplication(), null, 2)}</pre>
|
<pre className='my-8 overflow-y-auto text-xs'>{JSON.stringify(displayApplication(), null, 2)}</pre>
|
||||||
<button onClick={downloadZip} className='p-4 relative z-10 px-5 bg-emerald-600 m-2 rounded items-center text-white/70 flex cursor-pointer'>Download</button>
|
<button onClick={downloadZip} className='p-4 relative z-10 px-5 bg-cyan-700 border-t border-white/20 border-solid hover:bg-slate-900 transition m-2 rounded items-center text-white/70 flex cursor-pointer'>Download</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -189,7 +270,7 @@ export default function AddApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function App({ app, icon }) {
|
function App({ app, icon, inlineImage }) {
|
||||||
|
|
||||||
const [showDescription, setShowDescription] = useState(false);
|
const [showDescription, setShowDescription] = useState(false);
|
||||||
|
|
||||||
@ -217,16 +298,20 @@ function App({ app, icon }) {
|
|||||||
<div className={"rounded-xl group w-full shadow max-w-xs relative overflow-hidden h-[100px] border border-solid flex flex-col justify-between bg-slate-300 border-slate-400/50"}>
|
<div className={"rounded-xl group w-full shadow max-w-xs relative overflow-hidden h-[100px] border border-solid flex flex-col justify-between bg-slate-300 border-slate-400/50"}>
|
||||||
<div className={"absolute top-0 left-0 right-0 h-[200px] transition-all" + (showDescription ? ' -translate-y-1/2' : '')}>
|
<div className={"absolute top-0 left-0 right-0 h-[200px] transition-all" + (showDescription ? ' -translate-y-1/2' : '')}>
|
||||||
<div onClick={() => setShowDescription(true)} className={"h-[100px] p-4 relative overflow-hidden cursor-pointer"}>
|
<div onClick={() => setShowDescription(true)} className={"h-[100px] p-4 relative overflow-hidden cursor-pointer"}>
|
||||||
<img className="h-[90px] group-hover:scale-150 transition-all absolute left-2 top-1" src={app.image_src} alt={app.friendly_name} />
|
<img className="h-[90px] group-hover:scale-150 transition-all absolute left-2 top-1" src={app.image_src} onError={(e) => e.target.src = inlineImage} alt={app.friendly_name} />
|
||||||
<div className="flex-col pl-28">
|
<div className="flex-col pl-28">
|
||||||
<div className="font-bold">{app.friendly_name || 'Friendly Name'}</div>
|
<div className="font-bold">{app.friendly_name || 'Friendly Name'}</div>
|
||||||
<div className="text-xs mb-2 flex gap-2">{app.author || 'Unknown'} <span>{official()}</span></div>
|
<div className="text-xs mb-2 flex gap-2">{process.env.name || 'Unknown'} <span>{official()}</span></div>
|
||||||
<div className=" h-8"></div>
|
<div className=" h-8"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 bg-slate-400/20 h-8 text-[10px] flex items-center justify-center">
|
<div className="absolute bottom-0 left-0 right-0 bg-slate-400/20 h-8 text-[10px] flex items-center justify-center">
|
||||||
{app.categories.map(cat => (
|
{app.architecture && app.architecture.map((arch, index) => (
|
||||||
<span className="p-2 py-0 m-[1px] inline-block rounded bg-slate-300/90">{cat}</span>
|
<span key={'arch' + index} className="p-2 py-0 m-[1px] inline-block rounded bg-slate-400/70">{arch}</span>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{app.categories.map((cat, index) => (
|
||||||
|
<span key={'cat' + index} className="p-2 py-0 m-[1px] inline-block rounded bg-slate-300/90">{cat}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{appExists && appExists.enabled === true && appExists.available === false && (
|
{appExists && appExists.enabled === true && appExists.available === false && (
|
||||||
@ -235,7 +320,7 @@ function App({ app, icon }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="h-[100px] text-xs relative p-2 pl-4 flex">
|
<div className="h-[100px] text-xs relative p-2 pl-4 flex">
|
||||||
<button className="absolute right-2 top-2 bg-slate-100 rounded-full flex justify-center items-center h-6 w-6" onClick={() => setShowDescription(false)}>
|
<button className="absolute right-2 top-2 bg-slate-100 rounded-full flex justify-center items-center h-6 w-6" onClick={() => setShowDescription(false)}>
|
||||||
<svg style={{ height: '14px'}} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"/></svg>
|
<svg style={{ height: '14px' }} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z" /></svg>
|
||||||
</button>
|
</button>
|
||||||
<div className="flex flex-col flex-grow"><div className="font-bold">{app.friendly_name}</div> {app.description}</div>
|
<div className="flex flex-col flex-grow"><div className="font-bold">{app.friendly_name}</div> {app.description}</div>
|
||||||
<div className="flex flex-col justify-end gap-1">
|
<div className="flex flex-col justify-end gap-1">
|
@ -10,6 +10,10 @@ body {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
box-shadow: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
.bg-bubbles {
|
.bg-bubbles {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -610,6 +610,11 @@ acorn@^7.0.0:
|
|||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
|
acorn@6.4.1:
|
||||||
|
version "6.4.1"
|
||||||
|
resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz"
|
||||||
|
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
|
||||||
|
|
||||||
ajv@^6.10.0, ajv@^6.12.4:
|
ajv@^6.10.0, ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
||||||
@ -827,6 +832,11 @@ chokidar@^3.5.3:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
classnames@^2.1.1:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
|
||||||
|
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||||
|
|
||||||
client-only@0.0.1:
|
client-only@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
||||||
@ -1749,16 +1759,16 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||||
|
|
||||||
json5@^1.0.1:
|
json5@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz"
|
||||||
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
json5@^2.2.1:
|
json5@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.3"
|
||||||
resolved "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz"
|
resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
|
||||||
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
|
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||||
|
|
||||||
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
|
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
|
||||||
version "3.3.3"
|
version "3.3.3"
|
||||||
@ -2164,7 +2174,7 @@ process-nextick-args@~2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
|
||||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1:
|
prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
|
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
@ -2201,7 +2211,17 @@ react-is@^16.13.1, react-is@^16.7.0:
|
|||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
react-select@*:
|
react-notifications@^1.7.4:
|
||||||
|
version "1.7.4"
|
||||||
|
resolved "https://registry.npmjs.org/react-notifications/-/react-notifications-1.7.4.tgz"
|
||||||
|
integrity sha512-dsR7mUQfe8YdFLqVsjT0GFd4n26UWkzefdjMELfEVygjuuyU6ZZ0LpZhFHdfmraGeBFLWHNxygpGlHHituUyjQ==
|
||||||
|
dependencies:
|
||||||
|
acorn "6.4.1"
|
||||||
|
classnames "^2.1.1"
|
||||||
|
prop-types "^15.5.10"
|
||||||
|
react-transition-group "^4.4.1"
|
||||||
|
|
||||||
|
react-select@^5.6.1:
|
||||||
version "5.6.1"
|
version "5.6.1"
|
||||||
resolved "https://registry.npmjs.org/react-select/-/react-select-5.6.1.tgz"
|
resolved "https://registry.npmjs.org/react-select/-/react-select-5.6.1.tgz"
|
||||||
integrity sha512-dYNRswtxUHW+F1Sc0HnxO5ryecPIAsG0+Cwyq5EIXZJBxCxUG2hFfQz41tc++30/2ISuuPglDikc4hEb4NsiuA==
|
integrity sha512-dYNRswtxUHW+F1Sc0HnxO5ryecPIAsG0+Cwyq5EIXZJBxCxUG2hFfQz41tc++30/2ISuuPglDikc4hEb4NsiuA==
|
||||||
@ -2216,7 +2236,7 @@ react-select@*:
|
|||||||
react-transition-group "^4.3.0"
|
react-transition-group "^4.3.0"
|
||||||
use-isomorphic-layout-effect "^1.1.2"
|
use-isomorphic-layout-effect "^1.1.2"
|
||||||
|
|
||||||
react-transition-group@^4.3.0:
|
react-transition-group@^4.3.0, react-transition-group@^4.4.1:
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz"
|
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz"
|
||||||
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
||||||
|
Loading…
Reference in New Issue
Block a user