Merge branch 'rework/cards' into main

This commit is contained in:
Neshura 2023-05-03 23:38:13 +02:00
commit b42340dbda
Signed by: Neshura
GPG key ID: B6983AAA6B9A7A6C
19 changed files with 2123 additions and 265 deletions

View file

@ -11,29 +11,6 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
if(isMobile) { if(isMobile) {
ret = ( ret = (
<Page mobile={isMobile}> <Page mobile={isMobile}>
<Script id="matomo_analytics"
defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'>
</Script>
<Script id="matomo_analytics">
{`
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
_paq.push(["setCookieDomain", "www.neshweb.net"]);
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//tracking.neshweb.net/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '2']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
`}
</Script>
<PageNavbar mobile={isMobile}/> <PageNavbar mobile={isMobile}/>
<Main> <Main>
{children} {children}
@ -44,11 +21,6 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
else { else {
ret = ( ret = (
<Page> <Page>
<Script id="matomo_analytics"
defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'>
</Script>
<PageNavbar mobile={isMobile}/> <PageNavbar mobile={isMobile}/>
<Main> <Main>
{children} {children}

View file

@ -25,7 +25,7 @@ const PageNavbar = ({ mobile }: { mobile: number }) => {
<NavSideMenuPanel active={+sideBarActive}> <NavSideMenuPanel active={+sideBarActive}>
<NavBarMobile> <NavBarMobile>
{Links.links.map((item) => ( {Links.links.map((item) => (
<NavLinkMobile active={path === item.href ? +true : +false} key={item.name} href={item.href}> <NavLinkMobile active={path === item.href ? +true : +false} key={item.name} href={item.href}>
{item.name} {item.name}
</NavLinkMobile> </NavLinkMobile>
))} ))}
@ -42,7 +42,7 @@ const PageNavbar = ({ mobile }: { mobile: number }) => {
<NavWrapMobile> <NavWrapMobile>
<NavIndicators> <NavIndicators>
{Links.links.map((item) => ( {Links.links.map((item) => (
<NavIndicator active={path === item.href ? +true : +false} key={item.name} href={item.href} /> <NavIndicator active={path === item.href ? +true : +false} key={item.name} href={item.href} aria-label={item.name}/>
))} ))}
</NavIndicators> </NavIndicators>
</NavWrapMobile> </NavWrapMobile>

View file

@ -0,0 +1,325 @@
import { Service } from '../../../interfaces/CardTypes';
import styled, { css, DefaultTheme } from 'styled-components';
import Link from 'next/link';
import Image from 'next/image';
import OpenInNewTabIcon from '../../../public/icons/open-new-window.svg'
// needed for Online Status checks
interface OnlinePropType {
status: string;
}
interface BorderHelperType {
border_left: boolean;
}
const Card = styled.div<OnlinePropType>`
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 30rem;
max-width: 90%;
height: 12.5rem;
margin: 1rem;
// themeing
border-top: 0.25rem solid;
border-radius: 10px;
color: ${({ theme }) => theme.colors.primary};
border-color: ${props => {
let ret;
switch (props.status) {
case "Online":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.online;
break;
case "Loading":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.loading;
break;
case "Offline":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.offline;
break;
default:
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.offline;
}
return ret;
}};
background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background};
transition-property: max-height, margin-bottom;
transition-duration: 0.2s, 0s;
transition-delay: 0.2s, 0.2s;
`
// custom objects for CardTitle
//#############################
const CardHeaderWrap = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
max-height: 3.5rem;
flex-grow: 0.8;
`;
const CardTitleText = styled.h2`
font-size: 1.2rem;
margin: 0.5rem 0;
`;
const CardTitleIcon = styled.div`
position: relative;
object-fit: contain;
margin-right: 0.4rem;
aspect-ratio: 1;
height: 1.5rem;
`;
const CardStatus = styled.p<OnlinePropType>`
display: flex;
flex-direction: row;
font-size: 0.9rem;
padding: 0.1rem 0.3rem;
margin: 0.5rem;
margin-right: 1.5rem;
color: ${props => {
let ret;
switch (props.status) {
case "Online":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.online;
break;
case "Loading":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.loading;
break;
case "Offline":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.offline;
break;
default:
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.offline;
}
return ret;
}};
border-radius: 0.5rem;
border: 0;
border-bottom: 0.125rem solid;
background: transparent;
`
const CardTitle = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0.5rem;
padding-left: 1rem;
`
const CardTitleLink = styled(Link)`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0.5rem;
padding-left: 1rem;
&:hover {
color: ${({ theme }) => theme.colors.secondary};
}
`
// content visible when reduced
const CardHeader = ({ content }: { content: Service }) => {
return (
<CardHeaderWrap>
{
content.href ?
<CardTitleLink href={content.href}>
{
content.icon ? (
<CardTitleIcon>
<Image alt="icon" src={content.icon} fill sizes={'1.5rem'}/>
</CardTitleIcon>
) : (<></>)
}
<CardTitleText>{content.name}</CardTitleText>
<OpenInNewTab>
<OpenInNewTabIcon width="16px" height="16px" />
</OpenInNewTab>
</CardTitleLink> :
<CardTitle>
{
content.icon ? (
<CardTitleIcon>
<Image alt="icon" src={content.icon} fill sizes={'1.5rem'}/>
</CardTitleIcon>
) : (<></>)
}
<CardTitleText>{content.name}</CardTitleText>
</CardTitle>
}
<CardStatus status={content.status}>{content.status}</CardStatus>
</CardHeaderWrap>
)
}
// custom objects for CardDescription
//###################################
// shared properties for all Description objects
const CardDescriptionCommon = css`
text-align: left;
font-size: 0.9rem;
margin: 0.3rem;
`
// content visible when expanded
const CardDescriptionWrap = styled.div`
${CardDescriptionCommon}
padding: 0 1rem;
overflow: hidden; /* Hide scrollbars */
scrollbar-width: thin;
width: 100%;
`
const CardDescriptionExtended = styled.p`
${CardDescriptionCommon}
margin: 0;
margin-bottom: 0.9rem;
width: 100%;
`
const CardDescriptionWarning = styled(CardDescriptionExtended)`
text-align: center;
color: ${({ theme }) => theme.colors.offline};
font-weight: bold;
`
const CardDescription = ({ content }: { content: Service }) => {
return (
<CardDescriptionWrap>
<CardDescriptionExtended>
{content.desc}
</CardDescriptionExtended>
<CardDescriptionWarning>
{content.warn}
</CardDescriptionWarning>
</CardDescriptionWrap>
)
}
// custom objects for CardFooter
//##############################
const CardFooterWrap = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
grid-auto-flow: row;
//flex-direction: row;
align-items: center;
justify-items: center;
width: 100%;
position: absolute;
bottom: 5%;
`
const CardLink = styled(Link)`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0.5rem;
margin-left: 1rem;
margin-right: 1rem;
padding: 0 0.5rem;
border: 0;
border-radius: 5px;
border-left: 2px solid;
border-right: 2px solid;
transition-property: overflow-x;
transition-duration: 0.2s;
&:hover {
background-color: ${({ theme }) => theme.colors.background};
color: ${({ theme }) => theme.colors.secondary};
}
transition-property: background-color;
transition-duration: 0.5s;
`
const OpenInNewTab = styled.div`
color: ${({ theme }) => theme.colors.primary};
object-fit: contain;
aspect-ratio: 1;
height: 1rem;
width: 1rem;
max-width: 0;
overflow: hidden;
visibility: hidden;
${CardLink}:hover & {
margin-left: 0.3rem;
max-width: 1rem;
transition-delay: 0s, 0s;
visibility: visible;
color: ${({ theme }) => theme.colors.secondary};
}
${CardTitleLink}:hover & {
margin-left: 0.3rem;
max-width: 1rem;
transition-delay: 0s, 0s;
visibility: visible;
color: ${({ theme }) => theme.colors.secondary};
}
transition-property: max-width, margin-left, visibility;
transition-duration: 0.5s, 0s, 0S;
transition-delay: 0s, 0.2s, 0.2s;
`
const CardFooter = ({ content, href }: { content: Service, href: string }) => {
return (
<CardFooterWrap>
{href ? (
<CardLink href={href}>
Open
<OpenInNewTab>
<OpenInNewTabIcon width="16px" height="16px" />
</OpenInNewTab>
</CardLink>
) : (<></>)}
{content.extLink ? (
<CardLink href={content.extLink}>
Official Site
<OpenInNewTab>
<OpenInNewTabIcon width="16px" height="16px" />
</OpenInNewTab>
</CardLink>
) : (<></>)}
</CardFooterWrap>
)
}
// exported Card Elements
//#######################
export const ServiceCardDesktop = ({ content }: { content: Service }) => {
return (
<Card status={content.status}>
<CardHeader content={content} />
<CardDescription content={content} />
<CardFooter content={content} href={content.href ? content.href : ""} />
</Card>
)
}

View file

@ -0,0 +1,249 @@
import { Service } from '../../../interfaces/CardTypes';
import styled, { css, DefaultTheme } from 'styled-components';
import Link from 'next/link';
import Image from 'next/image';
import OpenInNewTabIcon from '../../../public/icons/open-new-window.svg'
// needed for Online Status checks
interface OnlinePropType {
status: string;
}
const Card = styled.div`
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 30rem;
max-width: 90%;
min-height: 10rem;
max-height: 12.5rem;
margin: 1rem;
// themeing
border-top: 0.25rem solid;
border-radius: 10px;
color: ${({ theme }) => theme.colors.primary};
border-color: ${({ theme }) => theme.colors.primary};
background-color: ${({ theme }) => theme.colors.background};
transition-property: max-height, margin-bottom;
transition-duration: 0.2s, 0s;
transition-delay: 0.2s, 0.2s;
`
// custom objects for CardTitle
//#############################
const CardTitleWrap = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
flex-grow: 0.8;
`;
const CardTitleText = styled.h2`
font-size: 1.2rem;
margin: 0.5rem 0;
`;
const CardTitleIcon = styled.div`
position: relative;
object-fit: contain;
margin-right: 0.4rem;
aspect-ratio: 1;
height: 1.5rem;
`;
const OpenInNewTab = styled.div`
color: ${({theme}) => theme.colors.primary };
position: relative;
object-fit: contain;
padding: 0.2rem;
margin-left: 0.5rem;
aspect-ratio: 1;
height: 1.5rem;
`
const CardStatus = styled.p<OnlinePropType>`
font-size: 0.9rem;
padding: 0.1rem;
margin: 0.5rem;
margin-right: 1.5rem;
border-radius: 5px;
border: 0;
border-bottom: 0.125rem solid;
background: transparent;
color: ${props => {
let ret;
switch (props.status) {
case "Online":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.online;
break;
case "Loading":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.loading;
break;
case "Offline":
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.offline;
break;
default:
ret = ({ theme }: { theme: DefaultTheme }) => theme.colors.offline;
}
return ret;
}};
`
const CardTitleLink = styled(Link)`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
align-self: flex-start;
margin: 0.5rem;
padding-left: 1rem;
`
const CardTitleLinkPlaceholder = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
align-self: flex-start;
margin: 0.5rem;
padding-left: 1rem;
`
// content visible when reduced
const CardTitle = ({ content, href }: { content: Service, href: string }) => {
let card;
if (href) {
card = (
<CardTitleWrap>
<CardTitleLink href={href}>
{
content.icon ? (
<CardTitleIcon>
<Image alt="icon" src={content.icon} fill sizes={'1.5rem'}/>
</CardTitleIcon>
) : (<></>)
}
<CardTitleText>{content.name}</CardTitleText>
{
<OpenInNewTab>
<OpenInNewTabIcon width="100%" height="100%"/>
</OpenInNewTab>
}
</CardTitleLink>
<CardStatus status={content.status}>{content.status}</CardStatus>
</CardTitleWrap>
)
}
else {
card = (
<CardTitleWrap>
<CardTitleLinkPlaceholder>
{
content.icon ? (
<CardTitleIcon>
<Image alt="icon" src={content.icon} fill sizes={'1.5rem'}/>
</CardTitleIcon>
) : (<></>)
}
<CardTitleText>{content.name}</CardTitleText>
</CardTitleLinkPlaceholder>
<CardStatus status={content.status}>{content.status}</CardStatus>
</CardTitleWrap>
)
}
return card
}
// custom objects for CardDescription
//###################################
// shared properties for all Description objects
const CardDescriptionCommon = css`
text-align: left;
font-size: 0.9rem;
margin: 0.3rem;
`
// content visible when expanded
const CardDescriptionWrap = styled.div`
${CardDescriptionCommon}
padding: 0 1rem;
margin-bottom: 2rem;
overflow: hidden; /* Hide scrollbars */
scrollbar-width: thin;
width: 100%;
p {
margin-top: 0;
margin-bottom: 0.9rem;
}
`
const CardDescriptionExtended = styled.p`
${CardDescriptionCommon}
margin: 0;
margin-bottom: 0.9rem;
width: 100%;
`
const CardDescription = ({ content }: { content: Service }) => {
let ret;
ret = (
<CardDescriptionWrap>
<CardDescriptionExtended>
{content.desc}
</CardDescriptionExtended>
<p>
{content.warn}
</p>
<a href={content.extLink}>
Official Site
</a>
</CardDescriptionWrap>
);
return ret;
}
// exported Card Elements
//#######################
export const ServiceCardMobile = ({ content }: { content: Service }) => {
let card;
// TEMP
if (content.href) {
card = (
<Card>
<CardTitle content={content} href={content.href}/>
<CardDescription content={content}/>
</Card>
)
}
else {
card = (
<Card>
<CardTitle content={content} href={""}/>
<CardDescription content={content}/>
</Card>
)
}
return card;
}

View file

@ -8,6 +8,7 @@ interface OnlinePropType {
status: string; status: string;
} }
// TODO: remove unneeded exports
// replaces .title // replaces .title
export const PageTitle = styled.h1` export const PageTitle = styled.h1`
margin: 0; margin: 0;
@ -33,13 +34,19 @@ export const PageDescription = styled.p`
// replaces .grid // replaces .grid
export const PageContentBox = styled.div` export const PageContentBox = styled.div`
display: flex; display: flex;
align-items: center; align-items: flex-start;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
max-width: 80%; width: 100%;
margin: 5.5rem;
`; `;
const CardStyle = css` // update for PageContentBox
export const PageContentBoxNew = styled(PageContentBox)`
gap: 2rem 1rem;
`
export const CardStyle = css`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -88,20 +95,18 @@ export const PageCard = styled.div`
${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & { ${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & {
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.secondary};
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ? background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background};
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
${CardLink}:focus,${CardLink}:active,${CardLink}:hover & { ${CardLink}:focus,${CardLink}:active,${CardLink}:hover & {
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.secondary};
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ? background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background};
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
`; `;
// replaces the three status classes // replaces the three status classes
const OnlineStatus = styled.p<OnlinePropType>` export const OnlineStatus = styled.p<OnlinePropType>`
color: ${props => { color: ${props => {
let ret; let ret;
switch (props.status) { switch (props.status) {
@ -139,25 +144,23 @@ const OnlineStatus = styled.p<OnlinePropType>`
${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & { ${CardStyleWrap}:focus,${CardStyleWrap}:active,${CardStyleWrap}:hover & {
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ? background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background};
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
${CardLink}:focus,${CardLink}:active,${CardLink}:hover & { ${CardLink}:focus,${CardLink}:active,${CardLink}:hover & {
border-color: ${({ theme }) => theme.colors.secondary}; border-color: ${({ theme }) => theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ? background-color: ${({ theme }) => theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background};
theme.colors.backgroundAlt ? theme.colors.backgroundAlt : theme.colors.background : theme.colors.background};
} }
`; `;
// replaces .cardwarn // replaces .cardwarn
const CardContentWarning = styled.p` export const CardContentWarning = styled.p`
color: ${({ theme }) => theme.colors.secondary}; color: ${({ theme }) => theme.colors.secondary};
`; `;
// replaces .contentIcon // replaces .contentIcon
const CardContentTitleIcon = styled(Image)` export const CardContentTitleIcon = styled(Image)`
object-fit: "contain"; object-fit: "contain";
margin-right: 8px; margin-right: 8px;
aspect-ratio: 1; aspect-ratio: 1;
@ -165,7 +168,7 @@ const CardContentTitleIcon = styled(Image)`
`; `;
// replaces .contentTitle // replaces .contentTitle
const CardContentTitleWrap = styled.div` export const CardContentTitleWrap = styled.div`
position: relative; position: relative;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -179,7 +182,7 @@ const CardContentTitleWrap = styled.div`
} }
`; `;
const CardContentTitle = ({ content }: { content: Service | Game }) => { export const CardContentTitle = ({ content }: { content: Service | Game }) => {
return ( return (
<CardContentTitleWrap> <CardContentTitleWrap>
{ {

View file

@ -19,6 +19,7 @@ export const Page = styled.div<MobilePropType>`
background-attachment: fixed; background-attachment: fixed;
background-size: cover; background-size: cover;
background-position: ${ props => props.mobile ? ({ theme }) => theme.backgroundOffset ? theme.backgroundOffset : "60%" : ""}; background-position: ${ props => props.mobile ? ({ theme }) => theme.backgroundOffset ? theme.backgroundOffset : "60%" : ""};
background-position-y: 0;
` `
export const Main = styled.main` export const Main = styled.main`

View file

@ -24,15 +24,12 @@ export const NavBar = styled.nav`
` `
export const NavLink = styled(Link) <ActivePropType>` export const NavLink = styled(Link) <ActivePropType>`
color: ${props => props.active ? color: ${props => ({ theme }) => props.active ?
({ theme }) => theme.invertButtons ? theme.colors.text ?
theme.colors.text ? theme.colors.text : theme.colors.secondary : theme.colors.secondary : theme.colors.text : theme.colors.secondary :
({ theme }) => theme.colors.primary}; theme.colors.primary};
background-color: ${props => props.active ? background-color: ${props => ({ theme }) => theme.colors.background};
({ theme }) => theme.invertButtons ?
theme.colors.secondary : theme.colors.background :
({ theme }) => theme.colors.background};
padding: 2px 6px; padding: 2px 6px;
border: 2px solid; border: 2px solid;
@ -43,12 +40,6 @@ export const NavLink = styled(Link) <ActivePropType>`
transition: all 0.1s ease; transition: all 0.1s ease;
&:hover { &:hover {
color: ${({ theme }) => theme.invertButtons ? color: ${({ theme }) => theme.colors.text ? theme.colors.text : theme.colors.secondary};
theme.colors.text ? theme.colors.text : theme.colors.primary :
theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ?
theme.colors.secondary :
theme.colors.background};
} }
` `

View file

@ -55,7 +55,6 @@ export const NavSideMenu = styled.div <ActivePropType>`
return ret; return ret;
}}; }};
backdrop-filter: ${props => props.active ? "blur(5px)" : ""}; backdrop-filter: ${props => props.active ? "blur(5px)" : ""};
overflow-x: hidden; overflow-x: hidden;
transition-property: max-width, max-height, border-right, background-color, backdrop-filter; transition-property: max-width, max-height, border-right, background-color, backdrop-filter;
transition-timing-function: ease-in-out; transition-timing-function: ease-in-out;
@ -88,12 +87,7 @@ export const NavSideMenuButton = styled.button <ActivePropType>`
color: ${props => ({ theme }) => { color: ${props => ({ theme }) => {
let ret: string; let ret: string;
if (props.active) { if (props.active) {
if (theme.invertButtons) { ret = theme.colors.secondary;
ret = theme.colors.text ? theme.colors.text : theme.colors.secondary;
}
else {
ret = theme.colors.secondary;
}
} }
else { else {
ret = theme.colors.primary; ret = theme.colors.primary;
@ -103,12 +97,7 @@ export const NavSideMenuButton = styled.button <ActivePropType>`
background-color: ${props => ({ theme }) => { background-color: ${props => ({ theme }) => {
let ret: string; let ret: string;
if (props.active) { if (props.active) {
if (theme.invertButtons) { ret = theme.colors.secondary;
ret = theme.colors.secondary;
}
else {
ret = theme.colors.background;
}
} }
else { else {
ret = theme.colors.background; ret = theme.colors.background;
@ -121,12 +110,7 @@ export const NavSideMenuButton = styled.button <ActivePropType>`
border-color: ${props => ({ theme }) => { border-color: ${props => ({ theme }) => {
let ret: string; let ret: string;
if (props.active) { if (props.active) {
if (theme.invertButtons) { ret = theme.colors.secondary;
ret = theme.colors.text ? theme.colors.text : theme.colors.secondary;
}
else {
ret = theme.colors.secondary;
}
} }
else { else {
ret = theme.colors.primary; ret = theme.colors.primary;
@ -136,36 +120,15 @@ export const NavSideMenuButton = styled.button <ActivePropType>`
&:hover { &:hover {
color: ${({ theme }) => { color: ${({ theme }) => {
let ret: string; return theme.colors.secondary
if (theme.invertButtons) {
ret = theme.colors.text ? theme.colors.text : theme.colors.primary;
}
else {
ret = theme.colors.secondary;
}
return ret;
}}; }};
background-color: ${({ theme }) => { background-color: ${({ theme }) => {
let ret: string; return theme.colors.background
if (theme.invertButtons) {
ret = theme.colors.secondary;
}
else {
ret = theme.colors.background;
}
return ret;
}}; }};
border-color: ${({ theme }) => { border-color: ${({ theme }) => {
let ret: string; return theme.colors.secondary;
if (theme.invertButtons) {
ret = theme.colors.text ? theme.colors.text : theme.colors.secondary;
}
else {
ret = theme.colors.secondary;
}
return ret;
}}; }};
} }
` `
@ -206,6 +169,7 @@ export const NavIndicators = styled.nav`
background-attachment: fixed; background-attachment: fixed;
background-size: cover; background-size: cover;
background-position: ${({ theme }) => theme.backgroundOffset ? theme.backgroundOffset : "60%"}; background-position: ${({ theme }) => theme.backgroundOffset ? theme.backgroundOffset : "60%"};
background-position-y: 0;
width: 100%; width: 100%;
display: flex; display: flex;
flex: 1; flex: 1;
@ -221,29 +185,15 @@ export const NavIndicator = styled(Link) <ActivePropType>`
aspect-ratio: 1; aspect-ratio: 1;
width: 10px; width: 10px;
border: 1px solid; border: 1px solid;
border-color: ${ props => ({ theme }) => props.active ? border-color: ${ props => ({ theme }) => props.active ? theme.colors.primary : theme.colors.primary};
theme.invertButtons ? theme.colors.secondary : theme.colors.primary :
theme.colors.primary
};
background-color: ${props => ({ theme }) => props.active ? background-color: ${props => ({ theme }) => props.active ? theme.colors.secondary : theme.colors.background};
theme.invertButtons ? theme.colors.secondary : theme.colors.primary :
theme.colors.background
};
&:hover { &:hover {
border-color: ${ props => ({ theme }) => border-color: ${({ theme }) => theme.colors.primary};
theme.invertButtons ? theme.colors.secondary : theme.colors.primary
};
color: ${({ theme }) => theme.invertButtons ? color: ${({ theme }) => theme.colors.primary};
theme.invertButtons ? theme.colors.secondary : theme.colors.primary :
theme.colors.primary
};
background-color: ${({ theme }) => theme.invertButtons ? background-color: ${({ theme }) => theme.colors.primary};
theme.colors.secondary :
theme.colors.primary
};
} }
` `

View file

@ -20,12 +20,12 @@ export const ThemeDropDownButton = styled.button<DisplayPropType>`
border: 2px solid; border: 2px solid;
border-radius: 5px; border-radius: 5px;
background-color: ${ props => props.focus ? background-color: ${ props => props.focus ?
({ theme }) => theme.invertButtons ? theme.colors.secondary : theme.colors.background : ({ theme }) => theme.colors.background :
({ theme }) => theme.colors.background}; ({ theme }) => theme.colors.background};
padding: 2px 6px; padding: 2px 6px;
cursor: pointer; cursor: pointer;
color: ${props => props.focus ? ({ theme }) => theme.invertButtons ? color: ${props => props.focus ?
theme.colors.text ? theme.colors.text : theme.colors.primary : theme.colors.secondary : ({ theme }) => theme.colors.text ? theme.colors.text : theme.colors.secondary :
({ theme }) => theme.colors.primary}; ({ theme }) => theme.colors.primary};
transition-property: color, border-bottom-left-radius, border-bottom-right-radius, background-color; transition-property: color, border-bottom-left-radius, border-bottom-right-radius, background-color;
@ -34,13 +34,9 @@ export const ThemeDropDownButton = styled.button<DisplayPropType>`
transition-delay: 0s, ${ props => props.show ? "0s, 0s" : "0.6s, 0.6s" }, 0s; transition-delay: 0s, ${ props => props.show ? "0s, 0s" : "0.6s, 0.6s" }, 0s;
&:focus,:hover { &:focus,:hover {
color: ${({ theme }) => theme.invertButtons ? color: ${({ theme }) => theme.colors.text ? theme.colors.text : theme.colors.secondary};
theme.colors.text ? theme.colors.text : theme.colors.primary :
theme.colors.secondary};
background-color: ${({ theme }) => theme.invertButtons ? background-color: ${({ theme }) => theme.colors.background};
theme.colors.secondary :
theme.colors.background};
} }
border-bottom-left-radius: ${ props => props.show ? "0" : "" }; border-bottom-left-radius: ${ props => props.show ? "0" : "" };

View file

@ -13,10 +13,11 @@ export interface Game {
export interface Service { export interface Service {
name: string, name: string,
icon: string, icon?: string,
href: string, href?: string,
desc: string, desc: string,
warn: string, warn?: string,
extLink?: string,
type: ServiceType, type: ServiceType,
docker_container_name: string, docker_container_name: string,
location: ServiceLocation, location: ServiceLocation,

View file

@ -5,6 +5,14 @@ const nextConfig = {
output: 'standalone', output: 'standalone',
compiler: { compiler: {
styledComponents: true, styledComponents: true,
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: [{ loader: "@svgr/webpack", options: { icon: true } }]
});
return config;
} }
}; };

View file

@ -10,7 +10,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"dockerode": "^3.3.4", "@svgr/webpack": "^6.5.1",
"eslint-config": "^0.3.0", "eslint-config": "^0.3.0",
"next": "^13.0.6", "next": "^13.0.6",
"react": "^18.2.0", "react": "^18.2.0",

View file

@ -1,4 +1,3 @@
import Docker from 'dockerode'
import ApiSecret from '../../private/portainer_api_secret.json' import ApiSecret from '../../private/portainer_api_secret.json'
import { DockerInfo } from '../../interfaces/DockerStatus'; import { DockerInfo } from '../../interfaces/DockerStatus';
import { ServiceLocation } from '../../interfaces/CardTypes'; import { ServiceLocation } from '../../interfaces/CardTypes';

View file

@ -1,35 +1,58 @@
import Head from 'next/head' import Head from 'next/head'
import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/CardTypes'; import { Service, Status, ServiceType } from '../interfaces/CardTypes';
import Dockerode from 'dockerode';
import { ReactElement } from 'react' import { ReactElement } from 'react'
import useSWR from 'swr'; import useSWR from 'swr';
import { CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content';
import ServiceList from '../public/data/pages.json'; import ServiceList from '../public/data/pages.json';
import { DockerInfo } from '../interfaces/DockerStatus'; import { DockerInfo } from '../interfaces/DockerStatus';
import { PageContentBox, PageDescription, PageTitle } from '../components/styles/content';
import useWindowSize from '../components/windowsize';
import { ServiceCardMobile } from '../components/styles/cards/mobile';
import { ServiceCardDesktop } from '../components/styles/cards/desktop';
const fetcher = (url: string) => fetch(url).then((res) => res.json()) const fetcher = (url: string) => fetch(url).then((res) => res.json())
function Services() { function Services() {
const { initialData, fullData, loadingFull, error } = useServices(); const { initialData, fullData, loadingFull, error } = useServices();
const isMobile = useWindowSize();
let content: ReactElement = <></>; let content: ReactElement = <></>;
if (error) { content = <div>Error loading data</div> } if (error) { content = <div>Error loading data</div> }
else if (loadingFull) { else if (loadingFull) {
content = if (isMobile) {
<PageContentBox> content =
{initialData?.map((item: Service) => ( <PageContentBox>
<CardContentService key={item.name} content={item} /> {initialData?.map((item: Service) => (
))} <ServiceCardMobile key={item.name} content={item} />
</PageContentBox> ))}
</PageContentBox>
}
else {
content =
<PageContentBox>
{initialData?.map((item: Service) => (
<ServiceCardDesktop key={item.name} content={item} />
))}
</PageContentBox>
}
} }
else if (fullData) { else if (fullData) {
content = if (isMobile) {
<PageContentBox> content =
{fullData.map((item: Service) => ( <PageContentBox>
<CardContentService key={item.name} content={item} /> {fullData.map((item: Service) => (
))} <ServiceCardMobile key={item.name} content={item} />
</PageContentBox> ))}
</PageContentBox>
}
else {
content =
<PageContentBox>
{fullData.map((item: Service) => (
<ServiceCardDesktop key={item.name} content={item} />
))}
</PageContentBox>
}
} }
else { else {
content = <div>Error loading data</div> content = <div>Error loading data</div>
@ -62,7 +85,7 @@ async function getStatus(entry: Service, containers: DockerInfo[]) {
// Others to follow but low prio as this is currently the only location used // Others to follow but low prio as this is currently the only location used
// Type APP // Type APP
if (entry.type === ServiceType.app) { if (entry.type === ServiceType.app && entry.href) {
await fetch(entry.href) await fetch(entry.href)
.then((response) => { .then((response) => {
if (response.ok) { if (response.ok) {
@ -103,10 +126,10 @@ async function getStatus(entry: Service, containers: DockerInfo[]) {
default: default:
console.log("Container Status " + container.status + " has no case implemented"); console.log("Container Status " + container.status + " has no case implemented");
entry.status = Status.offline; entry.status = Status.offline;
} }
found = true; found = true;
// cancel the for // cancel the for
break; break;
} }
} }
// If container name is not missing the container is set to offline // If container name is not missing the container is set to offline

View file

@ -3,9 +3,10 @@
{ {
"name": "Nextcloud", "name": "Nextcloud",
"icon": "/icons/nextcloud-logo.svg", "icon": "/icons/nextcloud-logo.svg",
"href": "https://nextcloud.neshweb.net", "href": "https://nextcloud.neshweb.net/",
"desc": "Self-hosted Cloud Storage Service", "desc": "Self-hosted Cloud Storage Service but longer and hopefully with wrap as well as some extra just to make sure",
"warn": "Note: Registration requires approval", "warn": "Note: Registration requires approval",
"extLink": "https://nextcloud.com/",
"type": "docker", "type": "docker",
"docker_container_name": "nextcloud", "docker_container_name": "nextcloud",
"location": "tower-0" "location": "tower-0"
@ -33,7 +34,7 @@
{ {
"name": "PeerTube", "name": "PeerTube",
"icon": "/icons/peertube-logo.svg", "icon": "/icons/peertube-logo.svg",
"href": "https://tube.neshweb.net", "href": "https://tube.neshweb.net/",
"desc": "Self-hosted PeerTube Instance", "desc": "Self-hosted PeerTube Instance",
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
@ -43,7 +44,7 @@
{ {
"name": "Mastodon", "name": "Mastodon",
"icon": "/icons/mastodon-logo.svg", "icon": "/icons/mastodon-logo.svg",
"href": "https://mastodon.neshweb.net", "href": "https://mastodon.neshweb.net/",
"desc": "Self-hosted Mastodon Instance", "desc": "Self-hosted Mastodon Instance",
"warn": "Note: Registration requires approval", "warn": "Note: Registration requires approval",
"type": "docker", "type": "docker",
@ -120,7 +121,7 @@
"location": "tower-0" "location": "tower-0"
}, },
{ {
"name": "Nginx Proxy Manager", "name": "Nginx",
"icon": "/icons/npm-logo.png", "icon": "/icons/npm-logo.png",
"href": "https://nginx.neshweb.net/", "href": "https://nginx.neshweb.net/",
"desc": "Web-based Nginx Proxy Manager", "desc": "Web-based Nginx Proxy Manager",
@ -129,6 +130,25 @@
"docker_container_name": "nginx-prox", "docker_container_name": "nginx-prox",
"location": "tower-0" "location": "tower-0"
}, },
{
"name": "Debug1",
"href": "https://qwant.com",
"desc": "Debug Debug Debug",
"warn": "Note: Debug",
"extLink": "https://qwant.com",
"type": "docker",
"docker_container_name": "matomo-web",
"location": "tower-0"
},
{
"name": "Debug2",
"desc": "Debug Debug Debug",
"warn": "Note: Debug",
"extLink": "https://qwant.com",
"type": "docker",
"docker_container_name": "Debug",
"location": "tower-0"
},
{ {
"name": "Proxmox", "name": "Proxmox",
"icon": "/icons/proxmox-logo.png", "icon": "/icons/proxmox-logo.png",

View file

@ -17,6 +17,7 @@
"themeId": 1, "themeId": 1,
"colors": { "colors": {
"background": "#161616", "background": "#161616",
"backgroundAlt": "#000000",
"primary": "#00AAFF", "primary": "#00AAFF",
"secondary": "#FF5500", "secondary": "#FF5500",
"online": "#2BFF00", "online": "#2BFF00",
@ -29,6 +30,7 @@
"themeId": 2, "themeId": 2,
"colors": { "colors": {
"background": "#000000", "background": "#000000",
"backgroundAlt": "#161616",
"primary": "#00AAFF", "primary": "#00AAFF",
"secondary": "#FF5500", "secondary": "#FF5500",
"online": "#2BFF00", "online": "#2BFF00",
@ -41,6 +43,7 @@
"themeId": 3, "themeId": 3,
"colors": { "colors": {
"background": "#000000", "background": "#000000",
"backgroundAlt": "#161616",
"primary": "#886aff", "primary": "#886aff",
"secondary": "#E1FF6A", "secondary": "#E1FF6A",
"online": "#00ff00", "online": "#00ff00",
@ -53,13 +56,10 @@
"themeId": 4, "themeId": 4,
"backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg", "backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg",
"backgroundOffset": "60%", "backgroundOffset": "60%",
"invertButtons": true,
"colors": { "colors": {
"background": "#0008", "background": "#0008",
"backgroundAlt": "#000d",
"primary": "#ccc", "primary": "#ccc",
"secondary": "#00C7C7", "secondary": "#00C7C7",
"text": "#000",
"online": "#00ff00", "online": "#00ff00",
"loading": "#0063C7", "loading": "#0063C7",
"offline": "#ff0000" "offline": "#ff0000"
@ -69,7 +69,6 @@
"themeName": "dev", "themeName": "dev",
"themeId": 5, "themeId": 5,
"backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg", "backgroundImage": "https://images4.alphacoders.com/112/1123390.jpg",
"invertButtons": true,
"colors": { "colors": {
"background": "#0000", "background": "#0000",
"backgroundAlt": "#0000", "backgroundAlt": "#0000",

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor">
<path d="M21 3h-6m6 0l-9 9m9-9v6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M21 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
</svg>

After

Width:  |  Height:  |  Size: 458 B

1
styled.d.ts vendored
View file

@ -6,7 +6,6 @@ declare module 'styled-components' {
themeId: number, themeId: number,
backgroundImage?: string, backgroundImage?: string,
backgroundOffset?: string, backgroundOffset?: string,
invertButtons?: boolean,
colors: { colors: {
background: string, background: string,
backgroundAlt?: string, backgroundAlt?: string,

1511
yarn.lock

File diff suppressed because it is too large Load diff