guild pages and header

This commit is contained in:
Corban-Lee Jones 2025-01-29 11:32:57 +00:00
parent 118c6f7991
commit 58c2eb9ac6
18 changed files with 531 additions and 209 deletions

View File

@ -79,3 +79,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
dark:bg-neutral-800
dark:border-neutral-700;
}
.guild-header-btn {
@apply text-sm text-gray-800 hover:text-blue-600 focus:outline-none focus:text-blue-600 dark:text-neutral-200 dark:hover:text-blue-500 dark:focus:text-blue-500;
}
.guild-header-btn.active {
@apply text-blue-600;
}

View File

@ -666,6 +666,10 @@ video {
top: 100%;
}
.z-10 {
z-index: 10;
}
.z-20 {
z-index: 20;
}
@ -759,18 +763,10 @@ video {
margin-inline-start: auto;
}
.mt-0\.5 {
margin-top: 0.125rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mt-12 {
margin-top: 3rem;
}
.mt-2 {
margin-top: 0.5rem;
}
@ -877,6 +873,11 @@ video {
height: 38px;
}
.size-\[46px\] {
width: 46px;
height: 46px;
}
.\!h-2\.5 {
height: 0.625rem !important;
}
@ -937,14 +938,14 @@ video {
min-width: 15rem;
}
.max-w-2xl {
max-width: 42rem;
}
.max-w-\[300px\] {
max-width: 300px;
}
.max-w-\[85rem\] {
max-width: 85rem;
}
.max-w-full {
max-width: 100%;
}
@ -961,6 +962,10 @@ video {
flex-shrink: 0;
}
.grow {
flex-grow: 1;
}
.basis-full {
flex-basis: 100%;
}
@ -996,10 +1001,6 @@ video {
resize: both;
}
.grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.flex-row {
flex-direction: row;
}
@ -1064,8 +1065,13 @@ video {
column-gap: 0.875rem;
}
.gap-y-4 {
row-gap: 1rem;
.gap-x-4 {
-moz-column-gap: 1rem;
column-gap: 1rem;
}
.gap-y-2 {
row-gap: 0.5rem;
}
.space-y-0\.5 > :not([hidden]) ~ :not([hidden]) {
@ -1281,6 +1287,11 @@ video {
padding-bottom: 0.125rem !important;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.px-2\.5 {
padding-left: 0.625rem;
padding-right: 0.625rem;
@ -1306,6 +1317,21 @@ video {
padding-right: 1.5rem;
}
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-1\.5 {
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.py-10 {
padding-top: 2.5rem;
padding-bottom: 2.5rem;
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
@ -1449,6 +1475,14 @@ video {
text-transform: lowercase;
}
.capitalize {
text-transform: capitalize;
}
.tracking-wide {
letter-spacing: 0.025em;
}
.text-blue-500 {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
@ -1459,11 +1493,6 @@ video {
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity, 1));
}
.text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity, 1));
@ -1489,11 +1518,6 @@ video {
color: rgb(22 163 74 / var(--tw-text-opacity, 1));
}
.text-red-500 {
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity, 1));
}
.text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity, 1));
@ -1504,10 +1528,6 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
}
.decoration-2 {
text-decoration-thickness: 2px;
}
.opacity-0 {
opacity: 0;
}
@ -1575,6 +1595,12 @@ video {
transition-duration: 150ms;
}
.transition-opacity {
transition-property: opacity;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
@ -1741,6 +1767,45 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
background-color: rgb(38 38 38 / var(--tw-bg-opacity, 1));
}
.guild-header-btn {
font-size: 0.875rem;
line-height: 1.25rem;
--tw-text-opacity: 1;
color: rgb(31 41 55 / var(--tw-text-opacity, 1));
}
.guild-header-btn:hover {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.guild-header-btn:focus {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
outline: 2px solid transparent;
outline-offset: 2px;
}
.guild-header-btn:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(229 229 229 / var(--tw-text-opacity, 1));
}
.guild-header-btn:hover:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
}
.guild-header-btn:focus:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
}
.guild-header-btn.active {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
}
.before\:absolute::before {
content: var(--tw-content);
position: absolute;
@ -1756,11 +1821,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
inset-inline-start: 0px;
}
.before\:me-6::before {
content: var(--tw-content);
margin-inline-end: 1.5rem;
}
.before\:h-4::before {
content: var(--tw-content);
height: 1rem;
@ -1771,22 +1831,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
width: 100%;
}
.before\:flex-1::before {
content: var(--tw-content);
flex: 1 1 0%;
}
.before\:border-t::before {
content: var(--tw-content);
border-top-width: 1px;
}
.before\:border-gray-200::before {
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));
}
.after\:absolute::after {
content: var(--tw-content);
position: absolute;
@ -1802,11 +1846,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
inset-inline-start: 0px;
}
.after\:ms-6::after {
content: var(--tw-content);
margin-inline-start: 1.5rem;
}
.after\:h-4::after {
content: var(--tw-content);
height: 1rem;
@ -1817,22 +1856,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
width: 100%;
}
.after\:flex-1::after {
content: var(--tw-content);
flex: 1 1 0%;
}
.after\:border-t::after {
content: var(--tw-content);
border-top-width: 1px;
}
.after\:border-gray-200::after {
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));
}
.hover\:bg-blue-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
@ -1863,20 +1886,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
color: rgb(31 41 55 / var(--tw-text-opacity, 1));
}
.hover\:underline:hover {
text-decoration-line: underline;
}
.focus\:border-blue-500:focus {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity, 1));
}
.focus\:bg-blue-700:focus {
--tw-bg-opacity: 1;
background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
}
.focus\:bg-gray-100:focus {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));
@ -1897,10 +1911,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
color: rgb(107 114 128 / var(--tw-text-opacity, 1));
}
.focus\:underline:focus {
text-decoration-line: underline;
}
.focus\:opacity-80:focus {
opacity: 0.8;
}
@ -1931,6 +1941,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
display: none;
}
.hs-dropdown.open > .hs-dropdown-open\:rotate-180 {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hs-dropdown.open > .hs-dropdown-open\:opacity-100 {
opacity: 1;
}
@ -1943,6 +1958,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
display: none;
}
.hs-dropdown.open > .hs-dropdown-toggle .hs-dropdown-open\:rotate-180 {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hs-dropdown.open > .hs-dropdown-toggle .hs-dropdown-open\:opacity-100 {
opacity: 1;
}
@ -1955,6 +1975,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
display: none;
}
.hs-dropdown.open > .hs-dropdown-menu > .hs-dropdown-open\:rotate-180 {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hs-dropdown.open > .hs-dropdown-menu > .hs-dropdown-open\:opacity-100 {
opacity: 1;
}
@ -1967,10 +1992,31 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
display: none;
}
.hs-dropdown-menu.open.hs-dropdown-open\:rotate-180 {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hs-dropdown-menu.open.hs-dropdown-open\:opacity-100 {
opacity: 1;
}
.hs-tooltip.show .hs-tooltip-shown\:visible {
visibility: visible;
}
.hs-tooltip.show .hs-tooltip-shown\:opacity-100 {
opacity: 1;
}
.hs-tooltip-content.show.hs-tooltip-shown\:visible {
visibility: visible;
}
.hs-tooltip-content.show.hs-tooltip-shown\:opacity-100 {
opacity: 1;
}
.hs-accordion.active.hs-accordion-active\:block {
display: block;
}
@ -2064,6 +2110,56 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
}
@media (min-width: 640px) {
.sm\:block {
display: block;
}
.sm\:flex {
display: flex;
}
.sm\:hidden {
display: none;
}
.sm\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.sm\:flex-row {
flex-direction: row;
}
.sm\:items-center {
align-items: center;
}
.sm\:justify-end {
justify-content: flex-end;
}
.sm\:justify-between {
justify-content: space-between;
}
.sm\:gap-6 {
gap: 1.5rem;
}
.sm\:gap-x-3 {
-moz-column-gap: 0.75rem;
column-gap: 0.75rem;
}
.sm\:gap-x-6 {
-moz-column-gap: 1.5rem;
column-gap: 1.5rem;
}
.sm\:gap-y-0 {
row-gap: 0px;
}
.sm\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
@ -2082,6 +2178,16 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.sm\:py-0 {
padding-top: 0px;
padding-bottom: 0px;
}
.sm\:text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
}
@media (min-width: 768px) {
@ -2109,6 +2215,10 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
-moz-column-gap: 0.75rem;
column-gap: 0.75rem;
}
.md\:p-5 {
padding: 1.25rem;
}
}
@media (min-width: 1024px) {
@ -2142,6 +2252,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
padding-right: 2rem;
}
.lg\:py-14 {
padding-top: 3.5rem;
padding-bottom: 3.5rem;
}
.lg\:ps-64 {
padding-inline-start: 16rem;
}
@ -2151,6 +2266,12 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
}
}
@media (min-width: 1280px) {
.xl\:grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
.dark\:divide-neutral-300:where(.dark, .dark *) > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: 1;
border-color: rgb(212 212 212 / var(--tw-divide-opacity, 1));
@ -2260,16 +2381,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
color: rgb(255 255 255 / 0.6);
}
.dark\:placeholder-neutral-500:where(.dark, .dark *)::-moz-placeholder {
--tw-placeholder-opacity: 1;
color: rgb(115 115 115 / var(--tw-placeholder-opacity, 1));
}
.dark\:placeholder-neutral-500:where(.dark, .dark *)::placeholder {
--tw-placeholder-opacity: 1;
color: rgb(115 115 115 / var(--tw-placeholder-opacity, 1));
}
.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity, 1));
@ -2280,28 +2391,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
color: rgb(163 163 163 / var(--tw-text-opacity, 1));
}
.dark\:before\:border-neutral-600:where(.dark, .dark *)::before {
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity, 1));
}
.dark\:after\:border-neutral-600:where(.dark, .dark *)::after {
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity, 1));
}
.dark\:checked\:border-blue-500:checked:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity, 1));
}
.dark\:checked\:bg-blue-500:checked:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));
}
.dark\:hover\:bg-neutral-700:hover:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity, 1));
@ -2362,10 +2451,6 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
--tw-ring-color: rgb(82 82 82 / var(--tw-ring-opacity, 1));
}
.dark\:focus\:ring-offset-gray-800:focus:where(.dark, .dark *) {
--tw-ring-offset-color: #1f2937;
}
.\[\&\:\:-webkit-scrollbar-thumb\]\:rounded-full::-webkit-scrollbar-thumb {
border-radius: 9999px;
}
@ -2392,4 +2477,4 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
.\[\&\:\:-webkit-scrollbar\]\:w-2::-webkit-scrollbar {
width: 0.5rem;
}
}

View File

@ -1,37 +0,0 @@
<% layout("layout") -%>
<h1 class="text-4xl">
<%= guild.name %>
</h1>
<h2 class="text-xl font-mono">
<%= guild.id %>
</h2>
<div class="grid grid-cols-4 gap-4">
<div>
<div>Members:</div>
<div><%= guild.memberCount %></div>
</div>
<div>
<div>Channels:</div>
<div><%= guild.channels.channelCountWithoutThreads %></div>
</div>
</div>
<div class="relative max-w-2xl mt-12">
<div class="bg-gray-900 text-white p-4 rounded-md">
<div class="overflow-x-auto">
<pre id="rawGuildData" class="text-gray-300">
<code>
<%= JSON.stringify(guild, null, 4) %>
</code>
</pre>
</div>
</div>
</div>

View File

@ -0,0 +1,7 @@
<% layout("layout") -%>
<%- include("guildHeader") -%>
<div class="p-4">
Content
</div>

View File

@ -0,0 +1,7 @@
<% layout("layout") -%>
<%- include("guildHeader") -%>
<div class="p-4">
Filters
</div>

View File

@ -0,0 +1,32 @@
<nav class="bg-white dark:bg-neutral-900">
<div class="max-w-[85rem] w-full mx-auto sm:flex sm:flex-row sm:justify-between sm:items-center sm:gap-x-3 py-3 px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center gap-x-3">
<div class="grow flex gap-x-4">
<% if (guild.icon) { %>
<img class="size-[28px] rounded" src="<%= guild.iconURL() %>" alt="<%= guild.name %>">
<% } else { %>
<div class="size-[28px] flex shrink-0 justify-center items-center rounded bg-neutral-100 dark:bg-neutral-800">
<span class="text-xs"><%= guild.name.split(" ").slice(0, 2).map(word => word[0].toUpperCase()).join(""); %></span>
</div>
<% } %>
<span class="font-semibold whitespace-nowrap text-gray-800 dark:text-neutral-200">
<%= guild.name %>
</span>
</div>
<button type="button" class="hs-collapse-toggle capitalize sm:hidden py-1.5 px-2 inline-flex items-center font-medium text-xs rounded-md border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 focus:outline-none focus:bg-gray-100 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700" data-hs-collapse="#guildScreenDropdown">
<%= !isNaN(+guildPage) ? 'Overview' : guildPage %>
<svg class="hs-dropdown-open:rotate-180 shrink-0 size-4 ms-1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
</button>
</div>
<div id="guildScreenDropdown" class="hs-collapse hidden overflow-hidden transition-all duration-300 basis-full grow sm:block">
<div class="py-2 sm:py-0 flex flex-col sm:flex-row sm:justify-end gap-y-2 sm:gap-y-0 sm:gap-x-6">
<a href="/guild/<%= guild.id %>" class="guild-header-btn <%= !isNaN(+guildPage) ? 'active' : '' %>">Overview</a>
<a href="/guild/<%= guild.id %>/subscriptions" class="guild-header-btn <%= guildPage === 'subscriptions' ? 'active' : '' %>">Subscriptions</a>
<a href="/guild/<%= guild.id %>/filters" class="guild-header-btn <%= guildPage === 'filters' ? 'active' : '' %>">Filters</a>
<a href="/guild/<%= guild.id %>/styles" class="guild-header-btn <%= guildPage === 'styles' ? 'active' : '' %>">Styles</a>
<a href="/guild/<%= guild.id %>/content" class="guild-header-btn <%= guildPage === 'content' ? 'active' : '' %>">Content</a>
</div>
</div>
</div>
</nav>

View File

@ -0,0 +1,122 @@
<% layout("layout") -%>
<%- include("guildHeader") -%>
<!-- Card Section -->
<div class="max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto">
<!-- Grid -->
<div class="grid sm:grid-cols-2 xl:grid-cols-4 gap-4 sm:gap-6">
<!-- Card -->
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="p-4 md:p-5 flex gap-x-4">
<div class="shrink-0 flex justify-center items-center size-[46px] bg-gray-100 rounded-lg dark:bg-neutral-800">
<svg class="shrink-0 size-5 text-gray-600 dark:text-neutral-400" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
</div>
<div class="grow">
<div class="flex items-center gap-x-2">
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-neutral-500">
Total Members
</p>
<div class="hs-tooltip">
<div class="hs-tooltip-toggle">
<svg class="shrink-0 size-4 text-gray-500 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
<span class="hs-tooltip-content hs-tooltip-shown:opacity-100 hs-tooltip-shown:visible opacity-0 transition-opacity inline-block absolute invisible z-10 py-1 px-2 bg-gray-900 text-xs font-medium text-white rounded shadow-sm dark:bg-neutral-700" role="tooltip">
The number of server members
</span>
</div>
</div>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl sm:text-2xl font-medium text-gray-800 dark:text-neutral-200">
<%= guild.memberCount %>
</h3>
<!-- <span class="flex items-center gap-x-1 text-green-600">
<svg class="inline-block size-4 self-center" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/></svg>
<span class="inline-block text-sm">
1.7%
</span>
</span> -->
</div>
</div>
</div>
</div>
<!-- End Card -->
<!-- Card -->
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="p-4 md:p-5 flex gap-x-4">
<div class="shrink-0 flex justify-center items-center size-[46px] bg-gray-100 rounded-lg dark:bg-neutral-800">
<svg class="shrink-0 size-6 text-gray-600 dark:text-neutral-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="9" x2="20" y2="9"></line><line x1="4" y1="15" x2="20" y2="15"></line><line x1="10" y1="3" x2="8" y2="21"></line><line x1="16" y1="3" x2="14" y2="21"></line></svg>
</div>
<div class="grow">
<div class="flex items-center gap-x-2">
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-neutral-500">
Channels
</p>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl sm:text-2xl font-medium text-gray-800 dark:text-neutral-200">
<%= guild.channels.channelCountWithoutThreads %>
</h3>
</div>
</div>
</div>
</div>
<!-- End Card -->
<!-- Card -->
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="p-4 md:p-5 flex gap-x-4">
<div class="shrink-0 flex justify-center items-center size-[46px] bg-gray-100 rounded-lg dark:bg-neutral-800">
<svg class="shrink-0 size-5 text-gray-600 dark:text-neutral-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>
</div>
<div class="grow">
<div class="flex items-center gap-x-2">
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-neutral-500">
Processed Content
</p>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl sm:text-2xl font-medium text-gray-800 dark:text-neutral-200">
0
</h3>
<!-- <span class="flex items-center gap-x-1 text-red-600">
<svg class="inline-block size-4 self-center" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 17 13.5 8.5 8.5 13.5 2 7"/><polyline points="16 17 22 17 22 11"/></svg>
<span class="inline-block text-sm">
1.7%
</span>
</span> -->
</div>
</div>
</div>
</div>
<!-- End Card -->
<!-- Card -->
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-neutral-800 dark:border-neutral-700">
<div class="p-4 md:p-5 flex gap-x-4">
<div class="shrink-0 flex justify-center items-center size-[46px] bg-gray-100 rounded-lg dark:bg-neutral-800">
<svg class="shrink-0 size-5 text-gray-600 dark:text-neutral-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line></svg>
</div>
<div class="grow">
<div class="flex items-center gap-x-2">
<p class="text-xs uppercase tracking-wide text-gray-500 dark:text-neutral-500">
Blocked Content
</p>
</div>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl sm:text-2xl font-medium text-gray-800 dark:text-neutral-200">
0
</h3>
</div>
</div>
</div>
</div>
<!-- End Card -->
</div>
<!-- End Grid -->
</div>
<!-- End Card Section -->

View File

@ -0,0 +1,7 @@
<% layout("layout") -%>
<%- include("guildHeader") -%>
<div class="p-4">
Styles
</div>

View File

@ -0,0 +1,7 @@
<% layout("layout") -%>
<%- include("guildHeader") -%>
<div class="p-4">
Subscriptions
</div>

View File

@ -1,44 +1,46 @@
<% layout("layout") -%>
<h1 class="text-4xl">Home Page</h1>
<p>
This is some placeholder text.
</p>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nulla aliquid temporibus sed odit blanditiis, repellendus id quibusdam voluptas harum vel culpa eligendi nihil corporis exercitationem ipsam a, deserunt voluptatibus soluta.
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Animi fugit necessitatibus assumenda eum. Sint est officiis ad laborum, dolores, illum explicabo error, doloribus repudiandae animi quis quam mollitia expedita fugiat!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Rerum asperiores provident et expedita ea unde vitae aut blanditiis, beatae sed rem a ad, quis quia dolorem ullam omnis magni sit?
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Optio ipsum voluptatibus enim laboriosam dolorem eveniet. Vitae cumque recusandae molestias molestiae necessitatibus nam quidem incidunt officia dignissimos, facilis, mollitia quaerat ad.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate hic odio non ad expedita itaque accusantium nostrum. Veritatis provident quaerat, quibusdam ullam possimus beatae corrupti repudiandae! Accusantium dolorem enim cupiditate?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur ullam exercitationem voluptas ducimus, veritatis repellat mollitia inventore recusandae tempore numquam voluptatibus dolores perferendis minima ut omnis, corrupti deserunt. Debitis, fuga?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero natus quod iure id cum a eligendi et laudantium in incidunt, consequatur perspiciatis aliquid aliquam eaque debitis deserunt. Alias, accusamus veniam.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, laudantium. Reprehenderit beatae autem illum et similique nesciunt suscipit? Quo dignissimos delectus minus, distinctio vero nesciunt iusto architecto accusamus eius iste.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolor harum vero, ut laboriosam error dolores itaque animi quo accusamus consectetur. Distinctio pariatur quo iste dolorem a nobis eum fuga delectus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit cumque rerum commodi aut modi maiores, incidunt suscipit odit? Natus aspernatur amet, est sapiente cupiditate consequuntur nam rerum autem accusamus culpa!
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Facere quaerat rem amet, laboriosam eius facilis minus pariatur magnam ea molestiae totam vitae suscipit veniam nihil explicabo voluptatibus sequi deleniti. Earum!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nam dolores exercitationem voluptas qui dolor consequuntur nostrum quidem, similique, incidunt voluptatum cum nulla. Quis odio ducimus cum eos saepe officiis at.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni ratione quaerat, sequi nostrum eius odio corrupti deserunt corporis, voluptates ex quas hic laborum non consequatur neque commodi, fuga inventore assumenda.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quibusdam aperiam distinctio numquam esse perferendis, placeat, pariatur consequatur nulla atque amet voluptatem molestiae quasi veniam quis sunt similique, modi ipsam! Consectetur!
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illo consequuntur neque numquam quam cumque dolores debitis dolorum exercitationem ratione optio fuga libero itaque, et ut. Tempore repellendus sit impedit illo.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Eveniet velit natus inventore unde rerum nemo, aliquam cum nihil mollitia eligendi. Iusto aliquam ea molestias, nobis nostrum expedita delectus neque saepe.
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quos odit sint cumque et perspiciatis debitis magnam inventore ratione magni, architecto sequi vel ipsum sunt id minus vitae necessitatibus, neque voluptatem!
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iste, delectus facere enim veritatis reiciendis esse non ex perspiciatis explicabo incidunt pariatur fuga impedit officiis doloribus commodi quam deleniti fugiat modi.
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iste odit consequuntur neque, voluptate aut similique cupiditate ex asperiores excepturi unde sed totam sapiente, placeat praesentium sit corrupti officia minima veritatis.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum rem quidem omnis fuga aspernatur, error qui maiores blanditiis rerum suscipit enim, totam vero, eligendi beatae laudantium inventore cumque mollitia impedit.
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Tenetur, maxime soluta consequuntur est ullam quia dolor, consequatur quasi ut porro ipsam! Maxime labore obcaecati dolorum, reprehenderit sunt repudiandae consequuntur assumenda.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vitae atque explicabo consequuntur quo totam similique magni laboriosam, reprehenderit quos deleniti pariatur ab esse sed fugiat accusantium veritatis, voluptas possimus ut?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio ducimus esse quae ad sapiente possimus quisquam dolorum ab error, voluptatum libero eveniet tenetur dignissimos id sunt, ullam, facere corrupti eaque.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus laborum id temporibus quis eveniet. Accusantium consequatur, beatae et facilis natus pariatur vitae aliquam possimus. Enim ratione odio harum eos hic?
</p>
<div class="p-4 sm:p-6 space-y-4 sm:space-y-6">
<h1 class="text-4xl">Home Page</h1>
<p>
This is some placeholder text.
</p>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nulla aliquid temporibus sed odit blanditiis, repellendus id quibusdam voluptas harum vel culpa eligendi nihil corporis exercitationem ipsam a, deserunt voluptatibus soluta.
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Animi fugit necessitatibus assumenda eum. Sint est officiis ad laborum, dolores, illum explicabo error, doloribus repudiandae animi quis quam mollitia expedita fugiat!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Rerum asperiores provident et expedita ea unde vitae aut blanditiis, beatae sed rem a ad, quis quia dolorem ullam omnis magni sit?
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Optio ipsum voluptatibus enim laboriosam dolorem eveniet. Vitae cumque recusandae molestias molestiae necessitatibus nam quidem incidunt officia dignissimos, facilis, mollitia quaerat ad.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate hic odio non ad expedita itaque accusantium nostrum. Veritatis provident quaerat, quibusdam ullam possimus beatae corrupti repudiandae! Accusantium dolorem enim cupiditate?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur ullam exercitationem voluptas ducimus, veritatis repellat mollitia inventore recusandae tempore numquam voluptatibus dolores perferendis minima ut omnis, corrupti deserunt. Debitis, fuga?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero natus quod iure id cum a eligendi et laudantium in incidunt, consequatur perspiciatis aliquid aliquam eaque debitis deserunt. Alias, accusamus veniam.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, laudantium. Reprehenderit beatae autem illum et similique nesciunt suscipit? Quo dignissimos delectus minus, distinctio vero nesciunt iusto architecto accusamus eius iste.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolor harum vero, ut laboriosam error dolores itaque animi quo accusamus consectetur. Distinctio pariatur quo iste dolorem a nobis eum fuga delectus.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit cumque rerum commodi aut modi maiores, incidunt suscipit odit? Natus aspernatur amet, est sapiente cupiditate consequuntur nam rerum autem accusamus culpa!
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Facere quaerat rem amet, laboriosam eius facilis minus pariatur magnam ea molestiae totam vitae suscipit veniam nihil explicabo voluptatibus sequi deleniti. Earum!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nam dolores exercitationem voluptas qui dolor consequuntur nostrum quidem, similique, incidunt voluptatum cum nulla. Quis odio ducimus cum eos saepe officiis at.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Magni ratione quaerat, sequi nostrum eius odio corrupti deserunt corporis, voluptates ex quas hic laborum non consequatur neque commodi, fuga inventore assumenda.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quibusdam aperiam distinctio numquam esse perferendis, placeat, pariatur consequatur nulla atque amet voluptatem molestiae quasi veniam quis sunt similique, modi ipsam! Consectetur!
</p>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illo consequuntur neque numquam quam cumque dolores debitis dolorum exercitationem ratione optio fuga libero itaque, et ut. Tempore repellendus sit impedit illo.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Eveniet velit natus inventore unde rerum nemo, aliquam cum nihil mollitia eligendi. Iusto aliquam ea molestias, nobis nostrum expedita delectus neque saepe.
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quos odit sint cumque et perspiciatis debitis magnam inventore ratione magni, architecto sequi vel ipsum sunt id minus vitae necessitatibus, neque voluptatem!
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iste, delectus facere enim veritatis reiciendis esse non ex perspiciatis explicabo incidunt pariatur fuga impedit officiis doloribus commodi quam deleniti fugiat modi.
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iste odit consequuntur neque, voluptate aut similique cupiditate ex asperiores excepturi unde sed totam sapiente, placeat praesentium sit corrupti officia minima veritatis.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum rem quidem omnis fuga aspernatur, error qui maiores blanditiis rerum suscipit enim, totam vero, eligendi beatae laudantium inventore cumque mollitia impedit.
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Tenetur, maxime soluta consequuntur est ullam quia dolor, consequatur quasi ut porro ipsam! Maxime labore obcaecati dolorum, reprehenderit sunt repudiandae consequuntur assumenda.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vitae atque explicabo consequuntur quo totam similique magni laboriosam, reprehenderit quos deleniti pariatur ab esse sed fugiat accusantium veritatis, voluptas possimus ut?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio ducimus esse quae ad sapiente possimus quisquam dolorum ab error, voluptatum libero eveniet tenetur dignissimos id sunt, ullam, facere corrupti eaque.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus laborum id temporibus quis eveniet. Accusantium consequatur, beatae et facilis natus pariatur vitae aliquam possimus. Enim ratione odio harum eos hic?
</p>
</div>

View File

@ -144,9 +144,7 @@
<!-- Content -->
<div class="w-full lg:ps-64">
<div class="p-4 sm:p-6 space-y-4 sm:space-y-6">
<%- body -%>
</div>
<%- body -%>
</div>
<!-- End Content -->

View File

@ -0,0 +1,17 @@
import { Request, Response } from "express";
import { client as bot } from "@bot/bot";
export const get = async (request: Request, response: Response) => {
const guildId = request.params.guildId;
const guild = bot.guilds.cache.get(guildId);
if (!guild) {
response.status(404).send("404: guild not found");
return;
}
response.render("guild/content", {
title: `${guild.name} - Relay`,
guild: guild,
});
};

View File

@ -0,0 +1,17 @@
import { Request, Response } from "express";
import { client as bot } from "@bot/bot";
export const get = async (request: Request, response: Response) => {
const guildId = request.params.guildId;
const guild = bot.guilds.cache.get(guildId);
if (!guild) {
response.status(404).send("404: guild not found");
return;
}
response.render("guild/filters", {
title: `${guild.name} - Relay`,
guild: guild,
});
};

View File

@ -0,0 +1,17 @@
import { Request, Response } from "express";
import { client as bot } from "@bot/bot";
export const get = async (request: Request, response: Response) => {
const guildId = request.params.guildId;
const guild = bot.guilds.cache.get(guildId);
if (!guild) {
response.status(404).send("404: guild not found");
return;
}
response.render("guild/overview", {
title: `${guild.name} - Relay`,
guild: guild,
});
};

View File

@ -10,7 +10,7 @@ export const get = async (request: Request, response: Response) => {
return;
}
response.render("guild", {
response.render("guild/styles", {
title: `${guild.name} - Relay`,
guild: guild,
});

View File

@ -0,0 +1,17 @@
import { Request, Response } from "express";
import { client as bot } from "@bot/bot";
export const get = async (request: Request, response: Response) => {
const guildId = request.params.guildId;
const guild = bot.guilds.cache.get(guildId);
if (!guild) {
response.status(404).send("404: guild not found");
return;
}
response.render("guild/subscriptions", {
title: `${guild.name} - Relay`,
guild: guild,
});
};

View File

@ -0,0 +1,7 @@
import { Request, Response, NextFunction } from "express";
export const getGuildPage = (request: Request, response: Response, next: NextFunction) => {
const currentPath = request.path.split("/");
response.locals.guildPage = currentPath[currentPath.length - 1];
next();
};

View File

@ -1,6 +1,15 @@
import { Router } from "express";
import * as controller from "@server/controllers/guild";
import * as overviewController from "@server/controllers/guild/overview";
import * as subscriptionController from "@server/controllers/guild/subscriptions";
import * as filterController from "@server/controllers/guild/filters";
import * as styleController from "@server/controllers/guild/styles";
import * as contentController from "@server/controllers/guild/content";
import { getGuildPage } from "@server/middleware/guildPage";
export const router = Router();
router.get("/:guildId", controller.get);
router.get("/:guildId", getGuildPage, overviewController.get);
router.get("/:guildId/subscriptions", getGuildPage, subscriptionController.get);
router.get("/:guildId/filters", getGuildPage, filterController.get);
router.get("/:guildId/styles", getGuildPage, styleController.get);
router.get("/:guildId/content", getGuildPage, contentController.get);