From 21825ee009a6dd4b09865ff116fdb4a112e1a4b0 Mon Sep 17 00:00:00 2001 From: Matt Beaudoin Date: Fri, 17 Apr 2026 12:34:40 -0700 Subject: [PATCH 1/2] add recipe from jjsfunhouse --- src/App.tsx | 2 + src/routes/Recipes/Recipe.scss | 0 src/routes/Recipes/Recipe.tsx | 0 src/routes/Recipes/RecipeForm.scss | 0 src/routes/Recipes/RecipeForm.tsx | 112 ++ src/routes/Recipes/Recipes.scss | 177 +++ src/routes/Recipes/Recipes.tsx | 192 ++++ src/services/mock-catalogue.json | 1726 ++++++++++++++++++++++++++++ 8 files changed, 2209 insertions(+) create mode 100644 src/routes/Recipes/Recipe.scss create mode 100644 src/routes/Recipes/Recipe.tsx create mode 100644 src/routes/Recipes/RecipeForm.scss create mode 100644 src/routes/Recipes/RecipeForm.tsx create mode 100644 src/routes/Recipes/Recipes.scss create mode 100644 src/routes/Recipes/Recipes.tsx create mode 100644 src/services/mock-catalogue.json diff --git a/src/App.tsx b/src/App.tsx index d424307..bfa3113 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { Apps } from './routes/Apps/Apps'; import { AppDetail } from './routes/Apps/App'; import { Servers } from './routes/Servers/Servers'; import { Server } from './routes/Servers/Server'; +import { Recipes } from './routes/Authenticated/Recipes/Recipes'; function App() { return ( @@ -15,6 +16,7 @@ function App() { } /> } /> } /> + } /> {/* 404 catch-all */} } /> diff --git a/src/routes/Recipes/Recipe.scss b/src/routes/Recipes/Recipe.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/Recipes/Recipe.tsx b/src/routes/Recipes/Recipe.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/Recipes/RecipeForm.scss b/src/routes/Recipes/RecipeForm.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/Recipes/RecipeForm.tsx b/src/routes/Recipes/RecipeForm.tsx new file mode 100644 index 0000000..4c29f00 --- /dev/null +++ b/src/routes/Recipes/RecipeForm.tsx @@ -0,0 +1,112 @@ +import { useState, useEffect } from "react"; +import { apiService } from '../../../services/api'; +import type { AbraServer } from '../../../types'; + + + +function RecipeForm({ recipe, onClose }) { + const [loading, setLoading] = useState(true); + const [servers, setServers] = useState([]); + const [selectedServer, setSelectedServer] = useState(""); // ❌ only one value + const [chaos, setChaos] = useState(false); + const [secrets, setSecrets] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + const fetchData = async () => { + try { + const [serversData] = await Promise.all([ + apiService.getServers(), + ]); + + setServers(serversData); + + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load servers'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }); + const [formData, setFormData] = useState({ + domain: "", + chaos: false, + secrets: true, + }); + + const handleChange = (e) => { + setFormData({ + ...formData, + [e.target.name]: e.target.value, + }); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + console.log("Submitting:", formData); + onClose(); + }; + + return ( +
+

{recipe.name}

+ { loading ? (

Loading servers...

+ ) : ( +
+ +
+ )} +
+ +
+
+ +
+ +
+ +
+ + + +
+ ); +} + +export default RecipeForm; \ No newline at end of file diff --git a/src/routes/Recipes/Recipes.scss b/src/routes/Recipes/Recipes.scss new file mode 100644 index 0000000..b2082bb --- /dev/null +++ b/src/routes/Recipes/Recipes.scss @@ -0,0 +1,177 @@ +@use '../../../assets/scss/variables' as *; +@use '../../../assets/scss/mixins' as *; +@use '../../../assets/scss/global' as *; + +// Extend global page wrapper +.recipes-page { + @extend .page-wrapper; +} + +.recipes-content { + @extend .page-content; +} + +// Servers grid +.recipes-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: $spacing-xl; + margin-bottom: $spacing-xl; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } +} + +// Server card +.recipe-card { + @include card; + display: grid; + grid-template-rows: 1fr 2fr auto 0.9fr; + transition: transform $transition-base, box-shadow $transition-base; + position: relative; + overflow: hidden; + + &:hover { + transform: translateY(-4px); + box-shadow: $shadow-xl; + } + + .recipe-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: $spacing-lg; + padding-bottom: $spacing-md; + border-bottom: 2px solid $bg-secondary; + + .recipe-title { + h3 { + margin: 0 0 $spacing-xs; + font-size: $font-size-xl; + color: $text-primary; + font-weight: $font-weight-bold; + } + + .recipe-host { + font-size: $font-size-sm; + color: $text-muted; + font-family: monospace; + } + } + img { + width: clamp(20px, 3vw, 32px); + height: clamp(20px, 3vw, 32px); + object-fit: contain; + } + } + .card-tags { + display: flex; + gap: 6px; + margin-top: $spacing-sm; + height: $spacing-md; + flex-wrap: wrap; + } + .tag-category { + font-size: 12px; + padding: 2px 8px; + border-radius: 5px; + color: rgb(255, 255, 255); + font-weight: 700; + background-color: rgb(111, 128, 255); + + } + .tag-feature { + font-size: 12px; + padding: 2px 8px; + border-radius: 5px; + color: rgb(255, 255, 255); + font-weight: 700; + background-color: rgb(22, 180, 22); + + } + .recipe-stats { + flex-grow: 1; + flex: 1; + margin-bottom: $spacing-lg; + + .stat-row { + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + + } + } + + .recipe-actions { + display: flex; + gap: $spacing-sm; + .action-btn { + flex: 1; + padding: $spacing-sm $spacing-md; + border: 2px solid $border-color; + background: none; + color: $text-primary; + border-radius: $radius-md; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + cursor: pointer; + transition: all $transition-base; + + &:hover { + background-color: rgba($primary, 0.1); + border-color: $primary; + transform: translateY(-1px); + } + + &.primary { + background-color: $primary; + color: white; + border-color: $primary; + + &:hover { + background-color: $primary-light; + border-color: $primary-light; + } + } + } + } + + .recipe-alert { + background-color: rgba($warning, 0.1); + border-left: 4px solid $warning; + padding: $spacing-sm $spacing-md; + margin: 0 (-$spacing-xl) (-$spacing-xl); + display: flex; + align-items: center; + gap: $spacing-sm; + + .alert-icon { + font-size: $font-size-lg; + color: $warning; + } + + .alert-text { + font-size: $font-size-sm; + color: $text-primary; + font-weight: $font-weight-medium; + } + } +} +.modal-overlay { +position: fixed; +inset: 0; +background: rgba(0, 0, 0, 0.4); +display: flex; +justify-content: center; +align-items: center; +} + +.modal { + background: white; + padding: 24px; + border-radius: 8px; + width: min(90%, 500px); +} diff --git a/src/routes/Recipes/Recipes.tsx b/src/routes/Recipes/Recipes.tsx new file mode 100644 index 0000000..d32946c --- /dev/null +++ b/src/routes/Recipes/Recipes.tsx @@ -0,0 +1,192 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Header } from '../../../components/Header/Header'; +import { apiService } from '../../../services/api'; +import type { AbraApp, AppWithServer, AbraRecipe } from '../../../types'; +import RecipeForm from './RecipeForm.tsx' +import './Recipes.scss'; + +export const Recipes: React.FC = () => { + const navigate = useNavigate(); + const [recipesData, setRecipesData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [searchTerm, setSearchTerm] = useState(''); + const [filterServer, setFilterServer] = useState('all'); + const [filterStatus, setFilterStatus] = useState('all'); + const [selectedRecipe, setSelectedRecipe] = useState(null); + const [showForm, setShowForm] = useState(false); + + + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + + useEffect(() => { + const fetchData = async () => { + try { + if (isMockMode) { + const { mockApiService } = await import('../../../services/mockApi'); + const data = await mockApiService.getRecipes(); + console.log(data) + setRecipesData(data); + } else { + const data = await apiService.getRecipes(); + setRecipesData(data); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load apps'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [isMockMode]); + + // Flatten and enrich apps data + const allRecipes: AbraRecipe[] = useMemo(() => { + if (!recipesData) return []; + + return recipesData; + }, [recipesData]); + + + // Filter apps + const filteredRecipes = useMemo(() => { + return allRecipes.filter(recipe => { + const matchesSearch = + recipe.name.toLowerCase().includes(searchTerm.toLowerCase()); + + return matchesSearch; + }); + }, [allRecipes, searchTerm, filterServer, filterStatus]); + + const stats = useMemo(() => { + const total = allRecipes.length; + + return { total }; + }, [allRecipes]); + + if (loading) { + return ( +
+
+
+
Loading applications...
+
+
+ ); + } + + if (error) { + return ( +
+
+
+
Error: {error}
+
+
+ ); + } + + return ( +
+
+
+
+

Applications

+

{stats.total} recipes

+
+ + {/* Stats Overview */} +
+
+
+

{stats.total}

+

Total Recipes

+
+
+
+ + {/* Filters */} +
+ setSearchTerm(e.target.value)} + className="search-input" + /> + + + +
+ + {/* Server Cards */} +
+ {filteredRecipes.length === 0 ? ( +
No recipes found matching your search
+ ) : ( + filteredRecipes.map((recipe) => ( +
+
+
+

{recipe.name}

+
+
+ {recipe.icon.length > 0 ? : null } +
+
+ +
+
+ {recipe.description} +
+
+ +
+ +
+
+ {recipe.features.backups.toLowerCase().includes("yes") ? Backups : null} + {recipe.features.healthcheck.toLowerCase().includes("yes") ? Healthcheck : null} + {recipe.category.length > 0 ? {recipe.category} : null} +
+
+ )) + )} +
+ {selectedRecipe && ( +
setSelectedRecipe(null)}> +
e.stopPropagation()}> + {} + setSelectedRecipe(null)} /> +
+
+ )} + +
+ Showing {filteredRecipes.length} of {allRecipes.length} apps +
+
+
+ ); +}; \ No newline at end of file diff --git a/src/services/mock-catalogue.json b/src/services/mock-catalogue.json new file mode 100644 index 0000000..3d11e40 --- /dev/null +++ b/src/services/mock-catalogue.json @@ -0,0 +1,1726 @@ +[ + { + "category": "Apps", + "default_branch": "main", + "description": "An awesome, Online Office suite image suitable for home use.", + "features": { + "backups": "No", + "email": "3", + "healthcheck": "No", + "image": { + "image": "collabora", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/collabora/code" + }, + "status": 3, + "tests": "2", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/249-0937028d43099df58d85bc41a7e69fbd", + "name": "collabora", + "repository": "https://git.coopcloud.tech/coop-cloud/collabora.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/collabora.git", + "versions": [ + { + "1.0.0+6.4.9.3": { + "app": { + "image": "collabora/code", + "tag": "6.4.9.3" + }, + "web": { + "image": "nginx", + "tag": "1.21.4" + } + } + }, + { + "2.0.0+21.11.0.5.1": { + "app": { + "image": "collabora/code", + "tag": "21.11.0.5.1" + }, + "web": { + "image": "nginx", + "tag": "1.21.4" + } + } + }, + { + "2.1.0+21.11.1.4.1": { + "app": { + "image": "collabora/code", + "tag": "21.11.1.4.1" + }, + "web": { + "image": "nginx", + "tag": "1.21.4" + } + } + }, + { + "2.1.2+22.05.8.2.1": { + "app": { + "image": "collabora/code", + "tag": "22.05.8.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.22.1" + } + } + }, + { + "2.2.0+22.05.10.1.1": { + "app": { + "image": "collabora/code", + "tag": "22.05.10.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.23.3" + } + } + }, + { + "2.3.0+22.05.14.3.1": { + "app": { + "image": "collabora/code", + "tag": "22.05.14.3.1" + }, + "web": { + "image": "nginx", + "tag": "1.24.0" + } + } + }, + { + "2.4.0+23.05.2.2.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.2.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.2" + } + } + }, + { + "2.5.0+23.05.3.1.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.3.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.2" + } + } + }, + { + "2.6.0+23.05.5.4.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.5.4.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.3" + } + } + }, + { + "2.7.0+23.05.8.2.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.8.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.4" + } + } + }, + { + "2.7.1+23.05.10.1.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.10.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.4" + } + } + }, + { + "3.0.0+24.04.6.1.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.6.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.27.0" + } + } + }, + { + "3.1.0+24.04.10.2.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.10.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.27.3" + } + } + }, + { + "3.2.0+24.04.12.3.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.12.3.1" + }, + "web": { + "image": "nginx", + "tag": "1.27.4" + } + } + }, + { + "3.3.0+24.04.13.3.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.13.3.1" + }, + "web": { + "image": "nginx", + "tag": "1.28.0" + } + } + }, + { + "4.0.0+25.04.4.1.1": { + "app": { + "image": "collabora/code", + "tag": "25.04.4.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.29.0" + } + } + }, + { + "4.0.1+25.04.9.1.1": { + "app": { + "image": "collabora/code", + "tag": "25.04.9.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.29.4" + } + } + }, + { + "4.0.2+25.04.9.4.1": { + "app": { + "image": "collabora/code", + "tag": "25.04.9.4.1" + }, + "web": { + "image": "nginx", + "tag": "1.29.7" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "HTTP/HTTPS compression proxy ", + "features": { + "backups": "No", + "email": "N/A", + "healthcheck": "No", + "image": { + "image": "compy", + "rating": "0", + "source": "own", + "url": "https://hub.docker.com/r/thecoopcloud/compy" + }, + "status": 1, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/435-127e3f583ac79a3b71f8fbd9c1cf5ba1", + "name": "compy", + "repository": "https://git.coopcloud.tech/coop-cloud/compy.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/compy.git", + "versions": [], + "website": "" + }, + { + "category": "Development", + "default_branch": "main", + "description": "A painless self-hosted Git service", + "features": { + "backups": "Yes", + "email": "Yes", + "healthcheck": "Yes", + "image": { + "image": "forgejo/forgejo", + "rating": "4", + "source": "upstream", + "url": "https://codeberg.org/forgejo/-/packages/container/forgejo/13-rootless" + }, + "status": 5, + "tests": "2", + "sso": "3 (OAuth)" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/eb3fb3ee823159765f746a75128143f3cd9b11c3f074ff6043ceeac8cdc434a9", + "name": "forgejo", + "repository": "https://git.coopcloud.tech/coop-cloud/forgejo.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/forgejo.git", + "versions": [ + { + "1.0.0+1.14.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.14.5-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.0+1.15.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.0-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.1+1.15.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.3-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.2+1.15.6-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.6-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.3+1.15.10-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.10-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.2.0+1.16.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.16.3-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.2.1+1.16.8-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.16.8-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.3.0+1.17.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.17.2-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.3.1+1.17.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.17.3-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.9" + } + } + }, + { + "2.0.0+1.18.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.18.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "9.6" + } + } + }, + { + "2.0.1+1.18.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.18.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "9.6" + } + } + }, + { + "2.1.0+1.18.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.18.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "9.6" + } + } + }, + { + "2.1.2+1.19.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.19.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.2" + } + } + }, + { + "2.2.0+1.19.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.19.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.3" + } + } + }, + { + "2.3.0+1.20.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.3" + } + } + }, + { + "2.3.1+1.20.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.3" + } + } + }, + { + "2.3.2+1.20.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.4" + } + } + }, + { + "2.3.3+1.20.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.4" + } + } + }, + { + "2.4.0+1.21.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.4" + } + } + }, + { + "2.5.0+1.21.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.5.1+1.21.4-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.4-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.5.2+1.21.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.6.0+1.21.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.6.1+1.21.10-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.10-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.6.2+1.21.10-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.10-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.7.0+1.21.11-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.11-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.6" + } + } + }, + { + "2.8.0+1.21.11-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.11-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.9.0+1.22.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.9.1+1.22.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.10.0+1.22.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.10.1+1.22.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "2.11.0+1.22.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "3.0.0+1.22.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "3.0.1+1.22.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "3.0.2+1.22.6-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.6-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.0.3+1.22.6-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.6-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.1.0+1.23.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.1.1+1.23.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.2.0+1.23.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.3.0+1.23.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.3.1+1.23.8-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.8-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.4.0+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "3.5.0+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "3.5.1+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "3.5.2+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "4.0.0+12.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "12.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "4.0.1+12.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "12.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "4.0.2+12.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "12.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.0+13.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.1+13.0.3-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.2+13.0.4-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.4-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.3+13.0.4-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.4-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.1.0+14.0.1-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "14.0.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.1.1+14.0.3-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "14.0.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "A shared agenda for local communities", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "gancio", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/cisti/gancio" + }, + "status": 0, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/485-f4a447197439adc13c2440728f1ba8b6", + "name": "gancio", + "repository": "https://git.coopcloud.tech/coop-cloud/gancio.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/gancio.git", + "versions": [ + { + "0.1.0+1.25.1": { + "app": { + "image": "cisti/gancio", + "tag": "1.25.1" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "A federated link aggregator in Rust", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "Yes", + "image": { + "image": "lemmy", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/dessalines/lemmy" + }, + "status": 1, + "tests": "2", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/432-5751cb07a13ead1bea8249a8f3b5ae68", + "name": "lemmy", + "repository": "https://git.coopcloud.tech/coop-cloud/lemmy.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/lemmy.git", + "versions": [ + { + "0.1.0+0.16.6": { + "app": { + "image": "dessalines/lemmy", + "tag": "0.16.6" + }, + "db": { + "image": "postgres", + "tag": "12-alpine" + }, + "pictrs": { + "image": "asonix/pictrs", + "tag": "0.3.0-beta.12-r1" + }, + "ui": { + "image": "dessalines/lemmy-ui", + "tag": "0.16.6" + }, + "web": { + "image": "nginx", + "tag": "1.20.0" + } + } + }, + { + "0.1.0+0.16.1": { + "app": { + "image": "dessalines/lemmy", + "tag": "0.16.1" + }, + "db": { + "image": "postgres", + "tag": "12-alpine" + }, + "pictrs": { + "image": "asonix/pictrs", + "tag": "0.3.0-beta.12-r1" + }, + "ui": { + "image": "dessalines/lemmy-ui", + "tag": "0.16.1" + }, + "web": { + "image": "nginx", + "tag": "1.20.0" + } + } + }, + { + "0.2.0+0.18.3": { + "app": { + "image": "dessalines/lemmy", + "tag": "0.18.3" + }, + "db": { + "image": "postgres", + "tag": "15-alpine" + }, + "pictrs": { + "image": "asonix/pictrs", + "tag": "0.4.0-beta.19" + }, + "ui": { + "image": "dessalines/lemmy-ui", + "tag": "0.18.3" + }, + "web": { + "image": "nginx", + "tag": "1.20.0" + } + } + } + ], + "website": "" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Spin up an onion service for your Co-op Cloud applications", + "features": { + "backups": "Yes", + "email": "N/A", + "healthcheck": "Yes", + "image": { + "image": "onimages/tor:alpine", + "rating": "4", + "source": "upstream", + "url": "https://onionservices.torproject.org/apps/base/containers" + }, + "status": 0, + "tests": "No", + "sso": "N/A" + }, + "icon": "", + "name": "onion", + "repository": "https://git.coopcloud.tech/coop-cloud/onion.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/onion.git", + "versions": [ + { + "1.0.0+alpine": { + "app": { + "image": "tpo/onion-services/onimages/tor", + "tag": "alpine" + } + } + }, + { + "1.0.1+alpine": { + "app": { + "image": "tpo/onion-services/onimages/tor", + "tag": "alpine" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "", + "features": { + "backups": "Yes", + "email": "Yes", + "healthcheck": "3", + "image": { + "image": "vaultwarden/server", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/vaultwarden/server" + }, + "status": 2, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/379-697fbe9f0f9481f250fde86e811c1e34", + "name": "vaultwarden", + "repository": "https://git.coopcloud.tech/coop-cloud/vaultwarden.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/vaultwarden.git", + "versions": [ + { + "0.1.0+1.25.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.25.0" + } + } + }, + { + "0.1.1+1.26.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.26.0" + } + } + }, + { + "0.2.0+1.26.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.26.0" + } + } + }, + { + "0.3.0+1.26.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.26.0" + } + } + }, + { + "0.4.0+1.28.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.28.1" + } + } + }, + { + "0.5.0+1.29.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.29.0" + } + } + }, + { + "0.5.1+1.29.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.29.1" + } + } + }, + { + "0.6.0+1.29.2": { + "app": { + "image": "vaultwarden/server", + "tag": "1.29.2" + } + } + }, + { + "0.7.0+1.30.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.30.0" + } + } + }, + { + "0.7.1+1.30.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.30.1" + } + } + }, + { + "0.7.2+1.30.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.30.3" + } + } + }, + { + "0.8.0+1.31.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.31.0" + } + } + }, + { + "0.9.0+1.32.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.0" + } + } + }, + { + "0.9.1+1.32.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.3" + } + } + }, + { + "1.0.0+1.32.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.3" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.1+1.32.5": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.5" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.2+1.32.5": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.5" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.3+1.32.5": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.5" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.4+1.32.7": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.7" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.0+1.33.2": { + "app": { + "image": "vaultwarden/server", + "tag": "1.33.2" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "2.0.0+1.33.2": { + "app": { + "image": "vaultwarden/server", + "tag": "1.33.2" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "2.1.0+1.34.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.34.1" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + }, + { + "2.1.1+1.34.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.34.3" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + }, + { + "2.1.2+1.35.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.35.3" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + }, + { + "2.1.3+1.35.4": { + "app": { + "image": "vaultwarden/server", + "tag": "1.35.4" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + } + ], + "website": "" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Easily and securely send things from one computer to another", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "croc", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/schollz/croc" + }, + "status": 3, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/365-d44b58c8c77808fb82c2d9fdfee17800", + "name": "croc", + "repository": "https://git.coopcloud.tech/coop-cloud/croc.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/croc.git", + "versions": [], + "website": "https://github.com/schollz/croc" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Community Keycloak SSO user management.", + "features": { + "backups": "No", + "email": "N/A", + "healthcheck": "Yes", + "image": { + "image": "", + "rating": "", + "source": "", + "url": "" + }, + "status": 1, + "tests": "Yes", + "sso": "N/A" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/281-ae6d28ba5409a428cbb3a49d5ef6d739", + "name": "keycloak-collective-portal", + "repository": "https://git.coopcloud.tech/coop-cloud/keycloak-collective-portal.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/keycloak-collective-portal.git", + "versions": [], + "website": "" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Manage a node in a local resilient mesh, see https://lores.tech/", + "features": { + "backups": "No", + "email": "N/A", + "healthcheck": "No", + "image": { + "image": "lores-node", + "rating": "4", + "source": "upstream", + "url": "https://ghcr.io/local-resilience-tech/lores-node:latest" + }, + "status": 1, + "tests": "0", + "sso": "N/A" + }, + "icon": "", + "name": "lores-node", + "repository": "https://git.coopcloud.tech/coop-cloud/lores-node.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/lores-node.git", + "versions": [ + { + "0.1.0+0.15.3": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.15.3" + } + } + }, + { + "0.2.0+v0.16.0": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.16.0" + } + } + }, + { + "0.2.1+v0.16.1": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.16.1" + } + } + }, + { + "0.2.2+v0.16.2": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.16.2" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "", + "features": { + "backups": "Yes", + "email": "No", + "healthcheck": "", + "image": { + "image": "plausible/analytics", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/plausible/analytics" + }, + "status": 1, + "tests": "", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/ba858ce9af5a5e69074b125fd4f99ac7b43fd1ad4fb82d926541175826037921", + "name": "plausible", + "repository": "https://git.coopcloud.tech/coop-cloud/plausible.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/plausible.git", + "versions": [ + { + "1.1.0+v1.5.1": { + "app": { + "image": "plausible/analytics", + "tag": "v1.5.1" + }, + "db": { + "image": "postgres", + "tag": "13.11-alpine" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + }, + { + "2.0.0+v1.5.1": { + "app": { + "image": "plausible/analytics", + "tag": "v1.5.1" + }, + "db": { + "image": "postgres", + "tag": "13.11" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + }, + { + "3.0.0+v2.0.0": { + "app": { + "image": "plausible/analytics", + "tag": "v2.0.0" + }, + "db": { + "image": "postgres", + "tag": "13.12" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + }, + { + "3.0.1+v2.0.0": { + "app": { + "image": "plausible/analytics", + "tag": "v2.0.0" + }, + "db": { + "image": "postgres", + "tag": "13.12" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + } + ], + "website": "https://github.com/plausible/analytics/" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "RSS feed for Docker Hub images", + "features": { + "backups": "?", + "email": "?", + "healthcheck": "?", + "image": { + "image": "docker-hub-rss", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/theconnman/docker-hub-rss/" + }, + "status": 0, + "tests": "?", + "sso": "?" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/210-09cd28347983de7287a1af0792371087", + "name": "docker-hub-rss", + "repository": "https://git.coopcloud.tech/coop-cloud/docker-hub-rss.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/docker-hub-rss.git", + "versions": [ + { + "0.1.0+0.4.2": { + "app": { + "image": "theconnman/docker-hub-rss", + "tag": "0.4.2" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "A web-based farm record keeping application. ", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "farmos", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/farmos" + }, + "status": 0, + "tests": "No", + "sso": "No" + }, + "icon": "", + "name": "farmos", + "repository": "https://git.coopcloud.tech/coop-cloud/farmos.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/farmos.git", + "versions": [], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "Funkwhale is a self-hosted, modern free and open-source music server, heavily inspired by Grooveshark.", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "funkwhale", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/funkwhale" + }, + "status": 0, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/455-5c7ee2a7d5ca18dbd1fba168994f9196", + "name": "funkwhale", + "repository": "https://git.coopcloud.tech/coop-cloud/funkwhale.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/funkwhale.git", + "versions": [ + { + "0.1.0+1.25.3": { + "api": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "app": { + "image": "nginx", + "tag": "1.25.3" + }, + "cache": { + "image": "redis", + "tag": "7-alpine" + }, + "celerybeat": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "celeryworker": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "db": { + "image": "postgres", + "tag": "10-alpine" + } + } + }, + { + "0.1.1+1.27.1": { + "api": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "app": { + "image": "nginx", + "tag": "1.27.1" + }, + "cache": { + "image": "redis", + "tag": "7-alpine" + }, + "celerybeat": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "celeryworker": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "db": { + "image": "postgres", + "tag": "10-alpine" + } + } + } + ], + "website": "" + } +] \ No newline at end of file -- 2.54.0 From f85b453b55afde6863ffc74d0840417ae808ba0b Mon Sep 17 00:00:00 2001 From: Matt Beaudoin Date: Fri, 17 Apr 2026 13:06:15 -0700 Subject: [PATCH 2/2] basic functionality working --- src/App.tsx | 4 ++-- src/routes/Recipes/RecipeForm.tsx | 4 ++-- src/routes/Recipes/Recipes.scss | 6 +++--- src/routes/Recipes/Recipes.tsx | 8 ++++---- src/services/api.ts | 7 ++++++- src/services/mockApi.ts | 9 ++++++++- src/types/index.ts | 32 +++++++++++++++++++++++++++++++ 7 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index bfa3113..6d37791 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ import { Apps } from './routes/Apps/Apps'; import { AppDetail } from './routes/Apps/App'; import { Servers } from './routes/Servers/Servers'; import { Server } from './routes/Servers/Server'; -import { Recipes } from './routes/Authenticated/Recipes/Recipes'; +import { Recipes } from './routes/Recipes/Recipes'; function App() { return ( @@ -17,7 +17,7 @@ function App() { } /> } /> } /> - + {/* 404 catch-all */} } /> diff --git a/src/routes/Recipes/RecipeForm.tsx b/src/routes/Recipes/RecipeForm.tsx index 4c29f00..dc3c7f9 100644 --- a/src/routes/Recipes/RecipeForm.tsx +++ b/src/routes/Recipes/RecipeForm.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; -import { apiService } from '../../../services/api'; -import type { AbraServer } from '../../../types'; +import { apiService } from '../../services/api'; +import type { AbraServer } from '../../types'; diff --git a/src/routes/Recipes/Recipes.scss b/src/routes/Recipes/Recipes.scss index b2082bb..ec084d7 100644 --- a/src/routes/Recipes/Recipes.scss +++ b/src/routes/Recipes/Recipes.scss @@ -1,6 +1,6 @@ -@use '../../../assets/scss/variables' as *; -@use '../../../assets/scss/mixins' as *; -@use '../../../assets/scss/global' as *; +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; // Extend global page wrapper .recipes-page { diff --git a/src/routes/Recipes/Recipes.tsx b/src/routes/Recipes/Recipes.tsx index d32946c..f8b9005 100644 --- a/src/routes/Recipes/Recipes.tsx +++ b/src/routes/Recipes/Recipes.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Header } from '../../../components/Header/Header'; -import { apiService } from '../../../services/api'; -import type { AbraApp, AppWithServer, AbraRecipe } from '../../../types'; +import { Header } from '../../components/Header/Header'; +import { apiService } from '../../services/api'; +import type { AbraApp, AppWithServer, AbraRecipe } from '../../types'; import RecipeForm from './RecipeForm.tsx' import './Recipes.scss'; @@ -25,7 +25,7 @@ export const Recipes: React.FC = () => { const fetchData = async () => { try { if (isMockMode) { - const { mockApiService } = await import('../../../services/mockApi'); + const { mockApiService } = await import('../../services/mockApi'); const data = await mockApiService.getRecipes(); console.log(data) setRecipesData(data); diff --git a/src/services/api.ts b/src/services/api.ts index 9afed3b..2f55c2d 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,4 +1,4 @@ -import type { AbraServer, ServerAppsResponse } from '../types'; +import type { AbraServer, AbraRecipe, ServerAppsResponse } from '../types'; // Log entry type export type LogEntry = { @@ -130,6 +130,11 @@ class ApiService { logs.forEach(log => onLog(log)); } } + // recipe catalog imports + async getRecipes(): Promise { + return this.request('/abra/catalogue'); + } } + export const apiService = new ApiService(); \ No newline at end of file diff --git a/src/services/mockApi.ts b/src/services/mockApi.ts index 709fe1d..71b776b 100644 --- a/src/services/mockApi.ts +++ b/src/services/mockApi.ts @@ -1,7 +1,9 @@ -import type { AbraServer, ServerAppsResponse } from '../types'; +import type { AbraServer, AbraRecipe, ServerAppsResponse } from '../types'; import appsData from './mock-apps.json'; import serversData from './mock-servers.json'; import logsData from './mock-logs.json'; +import catalogue from './mock-catalogue.json'; + // Log entry type export type LogEntry = { @@ -135,4 +137,9 @@ export const mockApiService = { onLog?.(log); } }, + // recipe catalog imports + async getRecipes(): Promise { + await delay(300); + return catalogue as AbraRecipe[]; + } }; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index b4e1d1d..8e2727f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -36,4 +36,36 @@ export interface AppWithServer extends AbraApp { appCount: number; upgradeCount: number; }; +} +export interface Image { + image: string; + rating: string + source: string + url: string +} +type RecipeVersions = Record>[]; +export interface ServiceMeta { + image: string; + tag: string +} +export interface Features { + backups: string; + email: string; + healthcheck: string; + image: Image; + status: number; + tests: string; + sso: string; +} +export interface AbraRecipe { + category: string; + default_branch: string; + description: string; + features: Features; + icon: string; + name: string; + repository: string; + ssh_url: string; + versions: RecipeVersions; + website: string; } \ No newline at end of file -- 2.54.0