diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..acb1cd06 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 22.12.0 \ No newline at end of file diff --git a/Makefile b/Makefile index c70d7c3e..f01d4a0b 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,35 @@ -dc-up: +SHELL := /bin/bash + +.PHONY: dc-up +dc-up: ## Bring up the stack docker compose up -d -dc-down: +.PHONY: dc-up-dev +dc-up-dev: ## Bring up the stack + docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d + +.PHONY: dc-down +dc-down: ## Bring down the stack docker compose down --volumes -dc-restart: +.PHONY: dc-restart +dc-restart: ## Restart the stack make dc-down make dc-up -dc-build: +.PHONY: dc-build +dc-build: ## Build Docker images docker compose build -dc-rebuild: +.PHONY: dc-rebuild +dc-rebuild: ## Rebuild Docker images and restart stack make dc-build - make dc-restart \ No newline at end of file + make dc-restart + +.PHONY: build-client +build-client: ## Builds the client locally + cd ./client && npm run build + +.PHONY: help +help: + @awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_\-\\.\/0-9]+:.*?##/ { printf " \033[36m%s:\033[0m%s\n", $$1, $$2 | "column -c2 -t -s :" }' $(MAKEFILE_LIST) \ No newline at end of file diff --git a/client/css/pool-list-view.styl b/client/css/pool-list-view.styl index b7ac15ed..d19fbf48 100644 --- a/client/css/pool-list-view.styl +++ b/client/css/pool-list-view.styl @@ -1,5 +1,64 @@ @import colors +.pools-grid + display: grid + grid-template-columns: repeat(4, 1fr) + gap: 16px + width: 100% + padding: 16px + box-sizing: border-box + + h1 + font-size: 1.5em + + .pool + width: 100% + aspect-ratio: 1 + display: flex + justify-content: center + align-items: center + background-color: #f0f0f0 + overflow: hidden + + .pool-inner-grid + display: grid + grid-template-columns: repeat(2, 1fr) + grid-template-rows: repeat(2, 1fr) + gap: 4px + width: 100% + height: 100% + position: relative + + div + aspect-ratio: 1 + position: relative + overflow: hidden + + img + width: 100% + height: 100% + object-fit: cover + position: absolute + top: 0 + left: 0 + + .pool-title + font-weight: 900 + aspect-ratio: auto + position: absolute + top: 50% + left: 50% + transform: translate(-50%, -50%) + background: rgba(0, 0, 0, 0.8) + color: white + font-size: 1.2rem + padding: 8px 16px + border-radius: 4px + text-align: center + pointer-events: none + z-index: 1 + + .pool-list table width: 100% diff --git a/client/html/pools_page.tpl b/client/html/pools_page.tpl index 0d811808..8f7aa179 100644 --- a/client/html/pools_page.tpl +++ b/client/html/pools_page.tpl @@ -1,3 +1,34 @@ +<% +function kebabToTitleCase(str) { + return str + .split('-') // Split the string into words using the hyphen as the delimiter + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) // Capitalize the first letter of each word + .join(' '); // Join the words back together with spaces +} +%> + +
+ <% if (ctx.response.results.length) { %> + <% for (const pool of ctx.response.results) { %> + '> +
+
+ <% let counter = 0 %> + <% for (const post of pool.posts) { %> +
+ <% counter++ %> + <% if (counter === 4) break %> + <% } %> +
<%= kebabToTitleCase(pool.names[0]) %> (<%- pool.postCount %>)
+
+
+
+ <% } %> + <% } else { %> +

No pools to display.

+ <% } %> +
+
<% if (ctx.response.results.length) { %> diff --git a/client/js/util/misc.js b/client/js/util/misc.js index 756ad84b..3e7e63c7 100644 --- a/client/js/util/misc.js +++ b/client/js/util/misc.js @@ -211,6 +211,13 @@ function getPrettyName(tag) { return tag; } +function kebabToTitleCase(str) { + return str + .split('-') // Split the string into words using the hyphen as the delimiter + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) // Capitalize the first letter of each word + .join(' '); // Join the words back together with spaces +} + module.exports = { range: range, formatRelativeTime: formatRelativeTime, @@ -229,4 +236,5 @@ module.exports = { escapeSearchTerm: escapeSearchTerm, dataURItoBlob: dataURItoBlob, getPrettyName: getPrettyName, + kebabToTitleCase: kebabToTitleCase, }; diff --git a/client/package.json b/client/package.json index 76376f82..28bd038e 100644 --- a/client/package.json +++ b/client/package.json @@ -3,7 +3,7 @@ "private": true, "scripts": { "build": "node build.js", - "watch": "node build.js --watch", + "watch": "node build.js --watch --no-live-reload", "build-container": "docker build -t szurubooru/client:dev ." }, "dependencies": { @@ -34,4 +34,4 @@ "watchify": "^4.0.0", "ws": "^7.4.6" } -} +} \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 00000000..193d9236 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,30 @@ +## Example Docker Compose configuration +## +## Use this as a template to set up docker-compose, or as guide to set up other +## orchestration services +services: + + client: + image: szurubooru/client:latest + build: client + depends_on: + - server + environment: + BACKEND_HOST: server + BASE_URL: + volumes: + - ./client/public:/var/www + - "${MOUNT_DATA}:/data:ro" + ports: + - "${PORT}:80" + + sql: + image: postgres:11-alpine + restart: unless-stopped + environment: + POSTGRES_USER: + POSTGRES_PASSWORD: + ports: + - 15432:5432 + volumes: + - "${MOUNT_SQL}:/var/lib/postgresql/data"