Signed-off-by: Max Schmidt <max.schmidt@outlook.de>
This commit is contained in:
Max Schmidt 2023-05-15 16:52:29 +02:00
parent a4b119614d
commit 7a57a69259
22 changed files with 1418 additions and 8897 deletions

1
astro/.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -1,4 +0,0 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

View File

@ -1,11 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@ -1,4 +1,18 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
import image from "@astrojs/image";
import compress from "astro-compress";
import critters from "astro-critters";
// https://astro.build/config // https://astro.build/config
export default defineConfig({}); export default defineConfig({
integrations: [
tailwind(),
image({
serviceEntryPoint: "@astrojs/image/sharp",
}),
compress(),
critters(),
],
});

8608
astro/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,21 @@
{ {
"name": "astro", "name": "astroad",
"type": "module", "type": "module",
"version": "0.0.1", "version": "0.0.1",
"license": "MIT",
"scripts": { "scripts": {
"dev": "astro dev --host", "dev": "astro dev --host",
"build": "astro build" "build": "astro build"
}, },
"dependencies": { "dependencies": {
"astro": "^2.4.1" "@astrojs/image": "^0.16.8",
"@astrojs/tailwind": "3.1.2",
"astro": "^2.4.5",
"astro-compress": "^1.1.43",
"astro-critters": "^1.1.34",
"css-select": "5.1.0",
"sharp": "^0.32.1",
"slate-serializers": "0.0.32",
"tailwindcss": "^3.0.24"
} }
} }

View File

@ -1,63 +0,0 @@
---
export interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 0.25rem;
background-color: white;
background-image: none;
background-size: 400%;
border-radius: 0.6rem;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: 1rem 1.3rem;
border-radius: 0.35rem;
color: #111;
background-color: white;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
color: #444;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent));
}
</style>

View File

@ -1,35 +1,19 @@
--- ---
export interface Props { export interface Props {
title: string; title: string;
} }
const { title } = Astro.props; const { title } = Astro.props;
--- ---
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="de">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} /> <title>{title}</title>
<title>{title}</title> </head>
</head> <body class="bg-teal-950 text-yellow-100">
<body> <slot />
<slot /> </body>
</body>
</html> </html>
<style is:global>
:root {
--accent: 124, 58, 237;
--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
}
html {
font-family: system-ui, sans-serif;
background-color: #F6F6F6;
}
code {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
</style>

View File

@ -1,91 +1,29 @@
--- ---
import Layout from '../layouts/Layout.astro'; import Layout from "../layouts/Layout.astro";
import Card from '../components/Card.astro'; import type { Post } from "../types";
import type { Post } from '../types';
const posts: Post[] = (await(await fetch("http://payload:3001/api/posts")).json()).docs; const posts: Post[] = (
posts.forEach(post=> console.log(post.title)); await (await fetch("http://payload:3001/api/posts")).json()
).docs;
posts.forEach((post) => console.log(post.title));
--- ---
<Layout title="Welcome to Astro."> <Layout title="Welcome to Astroad">
<main> <main class="flex flex-col items-center py-10 gap-5">
{posts.reverse().map(post => ( <h1>This is Astroad</h1>
<article> {
<h2>{post.title}</h2> posts ? (
<p>{post.updatedAt}</p> posts.reverse().map((post) => (
</article> <a href={`/posts/${post.id}`}>
))} <article class="text-teal-950 bg-yellow-100 px-5 py-3 rounded-md shadow-md w-64 text-center">
<h1>Welcome to <span class="text-gradient">Astro</span></h1> <h2>{post.title}</h2>
<p class="instructions"> <p>{new Date(post.updatedAt).toLocaleDateString("de-DE")}</p>
To get started, open the directory <code>src/pages</code> in your project.<br /> </article>
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above. </a>
</p> ))
<ul role="list" class="link-card-grid"> ) : (
<Card <p>No posts...</p>
href="https://docs.astro.build/" )
title="Documentation" }
body="Learn how Astro works and explore the official API docs." </main>
/>
<Card
href="https://astro.build/integrations/"
title="Integrations"
body="Supercharge your project with new frameworks and libraries."
/>
<Card
href="https://astro.build/themes/"
title="Themes"
body="Explore a galaxy of community-built starter themes."
/>
<Card
href="https://astro.build/chat/"
title="Community"
body="Come say hi to our amazing Discord community. ❤️"
/>
</ul>
</main>
</Layout> </Layout>
<style>
main {
margin: auto;
padding: 1.5rem;
max-width: 60ch;
}
h1 {
font-size: 3rem;
font-weight: 800;
margin: 0;
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
.instructions {
line-height: 1.6;
margin: 1rem 0;
border: 1px solid rgba(var(--accent), 25%);
background-color: white;
padding: 1rem;
border-radius: 0.4rem;
}
.instructions code {
font-size: 0.875em;
font-weight: bold;
background: rgba(var(--accent), 12%);
color: rgb(var(--accent));
border-radius: 4px;
padding: 0.3em 0.45em;
}
.instructions strong {
color: rgb(var(--accent));
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 1rem;
padding: 0;
}
</style>

View File

@ -0,0 +1,93 @@
---
import Layout from "../../layouts/Layout.astro";
import { Element } from "domhandler";
import type { Post } from "../../types";
import { Image } from "@astrojs/image/components";
import {
slateToHtml,
payloadSlateToDomConfig,
SlateToDomConfig,
} from "slate-serializers";
export async function getStaticPaths() {
const paths = (
await (await fetch("http://payload:3001/api/posts")).json()
).docs.map((post: Post) => ({
params: { id: post.id },
}));
return paths;
}
const { id } = Astro.params;
const post = (await (
await fetch(`http://payload:3001/api/posts/${id}`)
).json()) as Post;
const test: SlateToDomConfig = {
...payloadSlateToDomConfig,
elementTransforms: {
...payloadSlateToDomConfig.elementTransforms,
upload: ({ node }) =>
new Element("img", {
src: node.value.filename,
width: `${node.value.width}`,
height: `${node.value.height}`,
}),
},
};
const html = slateToHtml(post.content!, test).replaceAll(
"<p></p>",
"<p>&nbsp;</p>"
);
const htmlImageArray: (
| string
| { src: string; width: number; height: number }
)[] = [];
let lastIndex = 0;
while (true) {
const imgStartIndex = html.indexOf("<img", lastIndex);
if (imgStartIndex === -1) {
htmlImageArray.push(html.substring(lastIndex));
break;
}
const imgEndIndex = html.indexOf(">", imgStartIndex) + 1;
const imgTag = html.substring(imgStartIndex, imgEndIndex);
const remainingHtml = html.substring(lastIndex, imgStartIndex);
const imgObject = {
src: imgTag.match(/src="(.*?)"/)![1],
width: +imgTag.match(/width="(.*?)"/)![1],
height: +imgTag.match(/height="(.*?)"/)![1],
};
htmlImageArray.push(remainingHtml, imgObject);
lastIndex = imgEndIndex;
}
console.log(htmlImageArray);
---
<Layout title={post.title!}>
<h1>{id}</h1>
{
htmlImageArray.map((value) => {
if (typeof value === "string") {
return <div set:html={value} class="whitespace-pre-wrap" />;
} else {
return (
<Image
src={`http://payload:3001/media/${value.src}`}
width={value.width}
height={value.height}
format="webp"
alt="hallo"
/>
);
}
})
}
</Layout>
<style>
p:empty {
display: block;
content: "\00a0";
margin: 0;
padding: 0;
}
</style>

View File

@ -7,24 +7,16 @@
export interface Config { export interface Config {
collections: { collections: {
categories: Category;
posts: Post; posts: Post;
tags: Tag;
users: User; users: User;
media: Media;
}; };
globals: {}; globals: {};
} }
export interface Category {
id: string;
name?: string;
}
export interface Post { export interface Post {
id: string; id: string;
title?: string; title?: string;
author?: string | User;
publishedDate?: string; publishedDate?: string;
category?: string | Category;
tags?: string[] | Tag[];
content?: { content?: {
[k: string]: unknown; [k: string]: unknown;
}[]; }[];
@ -44,7 +36,14 @@ export interface User {
lockUntil?: string; lockUntil?: string;
password?: string; password?: string;
} }
export interface Tag { export interface Media {
id: string; id: string;
name?: string; updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
} }

View File

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}

View File

@ -1,3 +1,6 @@
{ {
"extends": "astro/tsconfigs/strict" "extends": "astro/tsconfigs/strict",
} "compilerOptions": {
"types": ["@astrojs/image/client"]
}
}

File diff suppressed because it is too large Load Diff

10
node_modules/.yarn-integrity generated vendored Normal file
View File

@ -0,0 +1,10 @@
{
"systemParams": "darwin-arm64-108",
"modulesFolders": [],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [],
"lockfileEntries": {},
"files": [],
"artifacts": {}
}

View File

@ -1,20 +0,0 @@
import { CollectionConfig } from 'payload/types';
const Categories: CollectionConfig = {
slug: 'categories',
admin: {
useAsTitle: 'name',
},
access: {
read: () => true,
},
fields: [
{
name: 'name',
type: 'text',
},
],
timestamps: false,
}
export default Categories;

View File

@ -0,0 +1,22 @@
import { CollectionConfig } from "payload/types";
export const Media: CollectionConfig = {
slug: "media",
labels: {
singular: "Bild",
plural: "Bilder",
},
admin: {},
access: {
read: (): boolean => true,
},
upload: {
staticURL: "/media",
staticDir: "media",
mimeTypes: ["image/*"],
},
fields: [],
};
export default Media;

View File

@ -3,7 +3,7 @@ import { CollectionConfig } from "payload/types";
const Posts: CollectionConfig = { const Posts: CollectionConfig = {
slug: "posts", slug: "posts",
admin: { admin: {
defaultColumns: ["title", "author", "category", "tags", "status"], defaultColumns: ["title", "author", "status"],
useAsTitle: "title", useAsTitle: "title",
}, },
access: { access: {
@ -14,29 +14,32 @@ const Posts: CollectionConfig = {
name: "title", name: "title",
type: "text", type: "text",
}, },
{
name: "author",
type: "relationship",
relationTo: "users",
},
{ {
name: "publishedDate", name: "publishedDate",
type: "date", type: "date",
}, },
{
name: "category",
type: "relationship",
relationTo: "categories",
},
{
name: "tags",
type: "relationship",
relationTo: "tags",
hasMany: true,
},
{ {
name: "content", name: "content",
type: "richText", type: "richText",
admin: {
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"],
leaves: ["bold", "italic", "underline"],
upload: {
collections: {
media: {
fields: [
{
name: "imagel",
type: "upload",
relationTo: "media",
required: true,
},
],
},
},
},
},
}, },
{ {
name: "status", name: "status",

View File

@ -1,20 +0,0 @@
import { CollectionConfig } from 'payload/types';
const Tags: CollectionConfig = {
slug: 'tags',
admin: {
useAsTitle: 'name',
},
access: {
read: () => true,
},
fields: [
{
name: 'name',
type: 'text',
},
],
timestamps: false,
}
export default Tags;

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

View File

@ -1,16 +1,15 @@
import { buildConfig } from "payload/config"; import { buildConfig } from "payload/config";
import path from "path"; import path from "path";
import Categories from "./collections/Categories";
import Posts from "./collections/Posts"; import Posts from "./collections/Posts";
import Tags from "./collections/Tags";
import Users from "./collections/Users"; import Users from "./collections/Users";
import Media from "./collections/Media";
export default buildConfig({ export default buildConfig({
serverURL: "http://localhost:3001", serverURL: "http://localhost:3001",
admin: { admin: {
user: Users.slug, user: Users.slug,
}, },
collections: [Categories, Posts, Tags, Users], collections: [Posts, Users, Media],
typescript: { typescript: {
outputFile: path.resolve("/", "types.ts"), outputFile: path.resolve("/", "types.ts"),
}, },

4
yarn.lock Normal file
View File

@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1