fix layout and updating works

Changed files
+137 -83
db
src
+35 -3
astro.config.mjs
··· 31 31 provider: fontProviders.fontsource(), 32 32 name: "IBM Plex Serif", 33 33 cssVariable: "--plex-serif", 34 - fallbacks: [ 'Charter', 'Bitstream Charter', 'Sitka Text', 'Cambria', 'Georgia', "serif"], 34 + }, 35 + { 36 + provider: fontProviders.fontsource(), 37 + name: "Libertinus Serif", 38 + cssVariable: "--libertinus-serif", 39 + }, 40 + { 41 + provider: fontProviders.fontsource(), 42 + name: "Noto Serif", 43 + cssVariable: "--noto-serif", 44 + }, 45 + { 46 + provider: fontProviders.fontsource(), 47 + name: "Lora", 48 + cssVariable: "--lora", 35 49 }, 36 50 { 37 51 provider: fontProviders.fontsource(), 38 - name: "IBM Plex Mono", 52 + name: "iA Writer Mono", 39 53 cssVariable: "--plex-mono", 54 + }, 55 + { 56 + provider: fontProviders.fontsource(), 57 + name: "Intel One Mono", 58 + cssVariable: "--intel-mono", 59 + }, 60 + { 61 + provider: fontProviders.fontsource(), 62 + name: "Recursive", 63 + cssVariable: "--recursive", 64 + styles: ["oblique", "normal"], 65 + weights: [300, 1000], 66 + variationSettings: "'slnt' -15 0, 'CASL' 0 1, 'CRSV' 0 1, 'MONO' 0 1", 40 67 }, 41 68 { 42 69 provider: fontProviders.fontsource(), ··· 95 122 style: "italic", 96 123 }, 97 124 ], 98 - } 125 + }, 126 + { 127 + provider: fontProviders.fontsource(), 128 + name: "Jacquard 12", 129 + cssVariable: "--jacquard-12", 130 + }, 99 131 ], 100 132 }, 101 133 });
+9
db/seed.ts
··· 4 4 await db.insert(Users).values([ 5 5 { id: 1, userDid: "test" }, 6 6 { id: 2, userDid: "another" }, 7 + { userDid: "did:plc:dg2qmmjic7mmecrbvpuhtvh6", nickname: "haetae" }, 7 8 ]); 8 9 9 10 await db.insert(Works).values([ ··· 21 22 content: "<p>whoag i have <b>BOLD</b></p>", 22 23 tags: [{ label: "label", url: "#" }], 23 24 }, 25 + { 26 + uri: "at://did:plc:dg2qmmjic7mmecrbvpuhtvh6/moe.fanfics.works/3lyeiyq32ek2o", 27 + slug: "1236", 28 + author: "did:plc:dg2qmmjic7mmecrbvpuhtvh6", 29 + title: "testing title", 30 + content: "what's up?! <b>bold</b> and <em>italics</em> should work.", 31 + tags: "hey", 32 + } 24 33 ]); 25 34 }
+1 -2
src/actions/works.ts
··· 158 158 }); 159 159 } 160 160 161 - // we'll just smush this in and pray 162 161 const result = await agent.com.atproto.repo.putRecord({ 163 162 repo: work.author, // since the author will be a did 164 163 collection: "moe.fanfics.works", ··· 167 166 title, 168 167 tags, 169 168 content, 169 + createdAt: work.createdAt.toISOString(), 170 170 updatedAt: updatedAt.toISOString(), 171 171 }, 172 172 validate: false, 173 - swapRecord: rkey, // idk what this does 174 173 }); 175 174 176 175 if (!result.success) {
+1 -17
src/assets/styles/global.css
··· 30 30 --text-7xl: clamp(7.4506rem, 71.4115rem + -82.5302cqi, 52.8422rem); 31 31 --text-8xl: clamp(9.3132rem, 116.6654rem + -138.5189cqi, 85.4986rem); 32 32 --text-9xl: clamp(11.6415rem, 190.1667rem + -230.355cqi, 138.3368rem); 33 - } 34 - 35 - @custom-variant dark (&:where( 36 - [data-theme=dark], 37 - [data-theme=dracula], 38 - [data-theme=synthwave], 39 - [data-theme=halloween], 40 - [data-theme=forest], 41 - [data-theme=aqua], 42 - [data-theme=black], 43 - [data-theme=luxury], 44 - [data-theme=business], 45 - [data-theme=night], 46 - [data-theme=coffee], 47 - [data-theme=sunset], 48 - [data-theme=abyss] 49 - )); 33 + }
+1 -1
src/layouts/Layout.astro
··· 32 32 <Navbar /> 33 33 </header> 34 34 35 - <div class:list={["min-w-[65ch] max-w-10/12 mx-auto text-base", className]} {...rest}> 35 + <div class:list={["sm:w-10/12 sm:mx-auto max-w-full mx-6 text-base", className]} {...rest}> 36 36 <slot /> 37 37 </div> 38 38
+2 -2
src/layouts/WorkPage.astro
··· 23 23 <a href="">previous chaptertitle</a> 24 24 )} 25 25 <!-- if theres more than one chapter, render this box --> 26 - <select name="chapterSelect" id={`${slug}-chapters`}> 26 + <select name="chapterSelect" id={`${slug}-chapters`} class="select"> 27 27 <option value="default" selected>Choose chapter...</option> 28 28 <!-- map each chapter here --> 29 29 </select> ··· 50 50 </div> 51 51 </header> 52 52 53 - <section id={`${slug}-content`} class="prose lg:prose-xl"> 53 + <section id={`${slug}-content`}> 54 54 <!-- if work has its own style, render it here somehow --> 55 55 <details> 56 56 <summary>Author's notes</summary>
+7
src/pages/errors/unauthorized.astro
··· 1 + --- 2 + import Layout from "@/layouts/Layout.astro"; 3 + --- 4 + <Layout> 5 + <h1>Unauthorized</h1> 6 + <p>You don't have the proper permissions to make that operation.</p> 7 + </Layout>
+1 -1
src/pages/not-found.astro src/pages/errors/not-found.astro
··· 1 1 --- 2 - import Layout from "../layouts/Layout.astro" 2 + import Layout from "@/layouts/Layout.astro"; 3 3 --- 4 4 <Layout> 5 5 <h1>Not found!</h1>
+62 -48
src/pages/works/[workId]/edit.astro
··· 16 16 .limit(1); 17 17 18 18 if (!work) { 19 - return Astro.redirect("/not-found"); 19 + return Astro.redirect("/errors/not-found"); 20 20 } 21 21 22 22 if (!loggedInUser) { ··· 24 24 } 25 25 26 26 if (work.Users.userDid !== loggedInUser.did) { 27 - return Astro.redirect("/unauthorized"); 27 + return Astro.redirect("/errors/unauthorized"); 28 28 } 29 29 30 30 const result = Astro.getActionResult(actions.worksActions.updateWork); ··· 33 33 <Layout skipLink={`edit-${workId}`}> 34 34 <main id={`edit-${workId}`}> 35 35 <form action={actions.worksActions.updateWork} method="post"> 36 - <label for="title">title</label> 37 - <input 38 - type="text" 39 - name="title" 40 - id="title" 41 - aria-describedby="title-error" 42 - value={work.Works.title} 43 - required 44 - transition:persist 45 - /> 46 - {errors.title && ( 47 - <div id="title-error"> 48 - {errors.title} 49 - </div> 50 - )} 36 + <fieldset class="fieldset"> 37 + <label for="title" class="label">Title</label> 38 + <input 39 + type="text" 40 + name="title" 41 + id="title" 42 + class="input w-full" 43 + aria-describedby="title-error" 44 + value={work.Works.title} 45 + required 46 + transition:persist 47 + /> 48 + {errors.title && ( 49 + <div id="title-error"> 50 + {errors.title} 51 + </div> 52 + )} 53 + </fieldset> 51 54 52 - <label for="tags">add tags</label> 53 - <input 54 - type="text" 55 - list="tags-list" 56 - name="tags" 57 - id="tags" 58 - aria-describedby="tags-error" 59 - value={work.Works.tags as string} 60 - transition:persist 61 - /> 62 - <!-- could be cool to fetch tags from a tags server or smth? idk --> 63 - <datalist id="tags-list"> 64 - <option value="test">here</option> 65 - <option value="tag2">another</option> 66 - <option value="tag3">try them all!</option> 67 - </datalist> 68 - {errors.tags && ( 69 - <div id="tags-error"> 70 - {errors.tags} 71 - </div> 72 - )} 55 + <fieldset class="fieldset"> 56 + <label for="tags" class="label">Add tags</label> 57 + <input 58 + type="text" 59 + list="tags-list" 60 + name="tags" 61 + id="tags" 62 + class="input" 63 + aria-describedby="tags-error" 64 + value={work.Works.tags as string} 65 + transition:persist 66 + /> 67 + <!-- could be cool to fetch tags from a tags server or smth? idk --> 68 + <datalist id="tags-list"> 69 + <option value="test">here</option> 70 + <option value="tag2">another</option> 71 + <option value="tag3">try them all!</option> 72 + </datalist> 73 + {errors.tags && ( 74 + <div id="tags-error"> 75 + {errors.tags} 76 + </div> 77 + )} 78 + </fieldset> 73 79 74 - <label for="content">body</label> 75 - <textarea name="content" id="content" aria-describedby="content-error" transition:persist> 76 - {work.Works.content} 77 - </textarea> 78 - {errors.content && ( 79 - <div id="content-error"> 80 - {errors.content} 81 - </div> 82 - )} 80 + <fieldset class="fieldset"> 81 + <label for="content" class="label">Body</label> 82 + <textarea 83 + name="content" 84 + id="content" 85 + class="textarea" 86 + aria-describedby="content-error" 87 + transition:persist 88 + > 89 + {work.Works.content.trim()} 90 + </textarea> 91 + {errors.content && ( 92 + <div id="content-error"> 93 + {errors.content} 94 + </div> 95 + )} 96 + </fieldset> 83 97 84 - <button>submit</button> 98 + <button class="btn btn-primary">Submit</button> 85 99 </form> 86 100 87 101 {result?.error && (
+7 -4
src/pages/works/[workId]/index.astro
··· 6 6 const { workId } = Astro.params; 7 7 const loggedInUser = Astro.locals.loggedInUser; 8 8 9 - // the work could be fetched from the database or from the pds 10 - // would this potentially lighten db load? maybe 11 9 const [work] = await db.select() 12 10 .from(Works) 13 11 .where(eq(Works.slug, workId!)) ··· 21 19 <WorkPage 22 20 slug={work.Works.slug} 23 21 title={work.Works.title} 24 - author={await didToHandle(work.Users.userDid)} 22 + author={work.Users.nickname 23 + ? work.Users.nickname 24 + : await didToHandle(work.Users.userDid)} 25 25 createdAt={work.Works.createdAt} 26 26 updatedAt={work.Works.updatedAt} 27 27 tags={work.Works.tags} ··· 29 29 {(work.Users.userDid === loggedInUser?.did) && ( 30 30 <a href={`/works/${workId}/edit`}>Edit your work</a> 31 31 )} 32 - <Fragment set:html={work.Works.content} /> 32 + 33 + <div class="prose lg:prose-xl"> 34 + <Fragment set:html={work.Works.content} /> 35 + </div> 33 36 </WorkPage>
+11 -5
src/pages/works/index.astro
··· 14 14 <Layout> 15 15 <h1 class="text-5xl">works</h1> 16 16 17 - <main id="content"> 17 + <main id="content" class="flex flex-col gap-5 max-w-full"> 18 18 {loggedInUser && 19 19 <a href="/works/add">wanna write kid?</a> 20 20 } 21 - 21 + 22 22 {works.map(async ({ Works, Users }) => ( 23 - <article class="card"> 23 + <article class="card rounded-box shadow p-5"> 24 24 <header> 25 25 <div class="flex items-center justify-between"> 26 26 <hgroup class="flex-1"> 27 27 <h2 class="card-title"> 28 28 <a href={`/works/${Works.slug}`}>{Works.title}</a> 29 29 </h2> 30 - <h3>{await didToHandle(Users.userDid)}</h3> 30 + <h3> 31 + {Users.nickname 32 + ? Users.nickname 33 + : await didToHandle(Users.userDid)} 34 + </h3> 31 35 </hgroup> 32 - <time datetime={Works.createdAt.toISOString()}>{Works.createdAt}</time> 36 + <time datetime={Works.createdAt.toISOString()}> 37 + {Works.createdAt.toLocaleDateString()} 38 + </time> 33 39 </div> 34 40 {JSON.stringify(Works.tags)} 35 41 {/* <ul class="flex flex-wrap gap-1.5">