un ottimo inizio
This commit is contained in:
commit
db1b50700f
21 changed files with 7144 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
node_modules
|
||||
|
||||
src/public
|
||||
*.log*
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
.output
|
||||
.env
|
||||
dist
|
||||
.vscode
|
||||
cosette.db
|
42
README.md
Normal file
42
README.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Nuxt 3 Minimal Starter
|
||||
|
||||
Look at the [nuxt 3 documentation](https://v3.nuxtjs.org) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install the dependencies:
|
||||
|
||||
```bash
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install --shamefully-hoist
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on http://localhost:3000
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.
|
10
app.vue
Normal file
10
app.vue
Normal file
|
@ -0,0 +1,10 @@
|
|||
<template>
|
||||
<div>
|
||||
<NuxtLayout>
|
||||
<Header/>
|
||||
<Hero v-show="$route.name === 'index'"/>
|
||||
<NuxtPage/>
|
||||
<Footer/>
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
19
components.d.ts
vendored
Normal file
19
components.d.ts
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
'IconIc:baselineAdd': typeof import('~icons/ic/baseline-add')['default']
|
||||
'IconMaterialSymbols:add': typeof import('~icons/material-symbols/add')['default']
|
||||
'IconSimpleIcons:add': typeof import('~icons/simple-icons/add')['default']
|
||||
'IconSimpleIcons:addthis': typeof import('~icons/simple-icons/addthis')['default']
|
||||
'IconSimpleIcons:nuxtdotjs': typeof import('~icons/simple-icons/nuxtdotjs')['default']
|
||||
'IconSimpleIcons:plus': typeof import('~icons/simple-icons/plus')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
19
components/cosetta.vue
Normal file
19
components/cosetta.vue
Normal file
|
@ -0,0 +1,19 @@
|
|||
<script setup>
|
||||
const { cosetta } = defineProps({
|
||||
cosetta: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="w-full md:w-1/3 xl:w-1/4 p-6 flex flex-col">
|
||||
<nuxt-link :to="`/c/${cosetta.name}`">
|
||||
<img class="hover:grow hover:shadow-lg" src="https://images.unsplash.com/photo-1555982105-d25af4182e4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80">
|
||||
<div class="pt-3 flex items-center justify-between">
|
||||
<p class=" text-black font-bold uppercase" v-text='cosetta.name'></p>
|
||||
</div>
|
||||
<p class="pt-1 text-gray-700 text-sm" v-text='cosetta.description'></p>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
29
components/footer.vue
Normal file
29
components/footer.vue
Normal file
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<footer class="container mx-auto bg-white py-8 border-t border-gray-400">
|
||||
<div class="container flex px-3 py-8 ">
|
||||
<div class="w-full mx-auto flex flex-wrap">
|
||||
<div class="flex w-full lg:w-1/2 ">
|
||||
<div class="px-3 md:px-0">
|
||||
<h4 class="font-bold text-xl text-gray-900">Cosette</h4>
|
||||
<p class="py-4">
|
||||
Liberati delle tue cosette
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full lg:w-1/2 lg:justify-end lg:text-right">
|
||||
<div class="px-3 md:px-0">
|
||||
<h3 class="font-bold text-xl text-gray-900">Links</h3>
|
||||
<ul class="list-reset items-center pt-3">
|
||||
<li>
|
||||
<a class="inline-block no-underline hover:text-black hover:underline py-1" href="https://it.hackmeeting.org">it.hackmeeting.org</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="inline-block no-underline hover:text-black hover:underline py-1" href="https://autistici.org/underscore">underscore_TO hacklab</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
39
components/header.vue
Normal file
39
components/header.vue
Normal file
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<nav id="header" class="w-full z-30 top-0 py-1">
|
||||
<div class="w-full container mx-auto flex flex-wrap items-center justify-between mt-0 px-6 py-3">
|
||||
|
||||
<label for="menu-toggle" class="cursor-pointer md:hidden block">
|
||||
<svg class="fill-current text-gray-900" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<title>menu</title>
|
||||
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path>
|
||||
</svg>
|
||||
</label>
|
||||
<input class="hidden" type="checkbox" id="menu-toggle" />
|
||||
|
||||
<div class="hidden md:flex md:items-center md:w-auto w-full order-3 md:order-1" id="menu">
|
||||
<nav>
|
||||
<ul class="md:flex items-center justify-between text-base text-gray-700 pt-4 md:pt-0">
|
||||
<li><a class="inline-block no-underline hover:text-black hover:underline py-2 px-4" href="#about">Ho una cosetta di cui liberarmi</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- <div class="order-1 md:order-2">
|
||||
</div> -->
|
||||
|
||||
<div class="order-2 md:order-3 flex items-center" id="nav-content">
|
||||
|
||||
<a class="flex items-center tracking-wide no-underline hover:no-underline font-bold text-gray-800 text-xl " href="#">
|
||||
COSETTE
|
||||
</a>
|
||||
<!-- <a class="pl-3 inline-block no-underline hover:text-black" href="#">
|
||||
<svg class="fill-current hover:text-black" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M21,7H7.462L5.91,3.586C5.748,3.229,5.392,3,5,3H2v2h2.356L9.09,15.414C9.252,15.771,9.608,16,10,16h8 c0.4,0,0.762-0.238,0.919-0.606l3-7c0.133-0.309,0.101-0.663-0.084-0.944C21.649,7.169,21.336,7,21,7z M17.341,14h-6.697L8.371,9 h11.112L17.341,14z" />
|
||||
<circle cx="10.5" cy="18.5" r="1.5" />
|
||||
<circle cx="17.5" cy="18.5" r="1.5" />
|
||||
</svg>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
11
components/hero.vue
Normal file
11
components/hero.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<section class="w-full mx-auto bg-nordic-gray-light flex pt-12 md:pt-0 md:items-center bg-cover bg-right" style="max-width:1600px; height: 32rem; background-image: url('https://picsum.photos/id/1072/3872/2592?grayscale')">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex flex-col w-full lg:w-1/2 justify-center items-start px-6 tracking-wide">
|
||||
<a class="text-xl inline-block no-underline border-b border-gray-600 leading-relaxed hover:text-black hover:border-black" href="#">cerco</a>
|
||||
<a class="text-xl inline-block no-underline border-b border-gray-600 leading-relaxed hover:text-black hover:border-black" href="#">offro</a>
|
||||
<h1 class="text-black text-2xl my-4">Cosette</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
37
components/upload.vue
Normal file
37
components/upload.vue
Normal file
|
@ -0,0 +1,37 @@
|
|||
<script setup>
|
||||
let cosetta = {}
|
||||
|
||||
function add () {
|
||||
const ret = $fetch('/api/cosette', { method: 'post', body: cosetta })
|
||||
console.error(ret)
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="modal" id="my-modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg">Aggiungi una cosetta!</h3>
|
||||
<p class="py-4">Ziobilly</p>
|
||||
<form action="/api/cosette" method="post" enctype="multipart/form-data">
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Alt label</span>
|
||||
</label>
|
||||
<input type="text" v-model='cosetta.name' name='name' placeholder="Name" class="input input-bordered w-full max-w-xs" />
|
||||
|
||||
<input type="file" name='imgs' />
|
||||
|
||||
<label class="label">
|
||||
<span class="label-text-alt">Description</span>
|
||||
</label>
|
||||
<textarea v-model='cosetta.description' name='description' class='textarea'></textarea>
|
||||
|
||||
|
||||
<div class="modal-action">
|
||||
<button class='btn btn-success' type='submit'>Add</button>
|
||||
<!-- <a @click='add' class='btn btn-success'>Add</a> -->
|
||||
<a href="#" class="btn">Close!</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
22
nuxt.config.ts
Normal file
22
nuxt.config.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { defineNuxtConfig } from 'nuxt'
|
||||
import UnpluginComponentsVite from 'unplugin-vue-components/vite'
|
||||
import IconsResolver from 'unplugin-icons/resolver'
|
||||
|
||||
// https://v3.nuxtjs.org/api/configuration/nuxt.config
|
||||
export default defineNuxtConfig({
|
||||
ssr: true,
|
||||
buildModules: ['@nuxtjs/tailwindcss', 'unplugin-icons/nuxt', '@nuxtjs/svg'],
|
||||
vite: {
|
||||
plugins: [
|
||||
UnpluginComponentsVite({
|
||||
dts: true,
|
||||
resolvers: [
|
||||
IconsResolver({
|
||||
prefix: 'Icon',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
})
|
23
package.json
Normal file
23
package.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.1.88",
|
||||
"@nuxtjs/svg": "^0.4.0",
|
||||
"@nuxtjs/tailwindcss": "^5.3.1",
|
||||
"daisyui": "^2.22.0",
|
||||
"nuxt": "3.0.0-rc.6",
|
||||
"unplugin-icons": "^0.14.8",
|
||||
"unplugin-vue-components": "^0.22.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^7.6.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
}
|
14
pages/c/[cosetta].vue
Normal file
14
pages/c/[cosetta].vue
Normal file
|
@ -0,0 +1,14 @@
|
|||
<script setup>
|
||||
const route = useRoute()
|
||||
|
||||
// qui devo prendere cosetta
|
||||
const cosette = await $fetch(`/api/cosetta/${$route.params.cosetta.id}`)
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<section class="bg-white py-8">
|
||||
<div class="container mx-auto flex items-center flex-wrap pt-4 pb-12">
|
||||
ciao - {{ $route.params.cosetta }}
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
32
pages/index.vue
Normal file
32
pages/index.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<script setup>
|
||||
const cosette = await $fetch('/api/cosette')
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<section class="bg-white py-8">
|
||||
|
||||
<Upload />
|
||||
|
||||
<div class="container mx-auto flex items-center flex-wrap pt-4 pb-12">
|
||||
<nav id="store" class="w-full z-30 top-0 px-6 py-1">
|
||||
<div class="w-full container mx-auto flex flex-wrap items-center justify-between mt-0 px-2 py-3">
|
||||
<a class="uppercase tracking-wide no-underline hover:no-underline font-bold text-yellow text-xl " href="https://it.hackmeeting.org">Hackmeeting 0x19</a>
|
||||
<div class="flex items-center" id="store-nav-content">
|
||||
<a class="pl-3 inline-block no-underline hover:text-black modal-button" href="#my-modal">
|
||||
<Icon-ic:baseline-add/>
|
||||
</a>
|
||||
<a class="pl-3 inline-block no-underline hover:text-black" href="#">
|
||||
<svg class="fill-current hover:text-black" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M10,18c1.846,0,3.543-0.635,4.897-1.688l4.396,4.396l1.414-1.414l-4.396-4.396C17.365,13.543,18,11.846,18,10 c0-4.411-3.589-8-8-8s-8,3.589-8,8S5.589,18,10,18z M10,4c3.309,0,6,2.691,6,6s-2.691,6-6,6s-6-2.691-6-6S6.691,4,10,4z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<Cosetta v-for='cosetta in cosette' :key='cosetta.id' :cosetta='cosetta' />
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</template>
|
6
server/api/cosetta/[name].js
Normal file
6
server/api/cosetta/[name].js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { get } from '../../controller'
|
||||
|
||||
export default defineEventHandler(event => {
|
||||
console.error(event)
|
||||
return get(event.context.params.name)
|
||||
})
|
6
server/api/cosette.js
Normal file
6
server/api/cosette.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { getAll } from '../controller'
|
||||
|
||||
export default defineEventHandler(event => {
|
||||
console.error(event)
|
||||
return getAll()
|
||||
})
|
16
server/api/cosette.post.js
Normal file
16
server/api/cosette.post.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { add } from '../controller'
|
||||
import { useBody, callHandler } from 'h3'
|
||||
import { uploadService } from '../services/upload-service'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const handler = uploadService().generateHandler()
|
||||
await callHandler(handler, event.req, event.res)
|
||||
const body = await useBody(event)
|
||||
body.imgs = [event.req.file?.filename?]
|
||||
add(body)
|
||||
return { success: true }
|
||||
} catch (e) {
|
||||
return { success: false, reason: e.message }
|
||||
}
|
||||
})
|
35
server/controller.js
Normal file
35
server/controller.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import Database from 'better-sqlite3'
|
||||
import { v4 } from 'uuid'
|
||||
const db = new Database('./cosette.db')
|
||||
|
||||
function load () {
|
||||
db.pragma('journal_mode = WAL')
|
||||
db.exec('CREATE TABLE IF NOT EXISTS cosette (uuid TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, tags TEXT, images TEXT)')
|
||||
}
|
||||
// load()
|
||||
|
||||
export function add (cosetta) {
|
||||
const q = db.prepare('INSERT INTO cosette (uuid, name, description, tags, images) VALUES(:uuid, :name, :description, :tags, :imgs)')
|
||||
cosetta.uuid = v4()
|
||||
cosetta.tags = JSON.stringify(cosetta.tags)
|
||||
cosetta.imgs = JSON.stringify(cosetta.imgs)
|
||||
q.run(cosetta)
|
||||
return cosetta
|
||||
}
|
||||
|
||||
export function get (uuid) {
|
||||
const q = db.prepare('SELECT * from cosette WHERE uuid = ?')
|
||||
console.error('sono dentro cosetta e cerco ', uuid)
|
||||
q.get(uuid)
|
||||
}
|
||||
|
||||
export function getAll (limit=0, offset=0, tags=[], search='') {
|
||||
const q = db.prepare('SELECT * FROM cosette')
|
||||
const ret = q.all()
|
||||
return ret.map(r => {
|
||||
r.tags = JSON.parse(r.tags)
|
||||
r.imgs = JSON.parse(r.imgs)
|
||||
return r
|
||||
})
|
||||
}
|
||||
|
48
server/services/upload-service.ts
Normal file
48
server/services/upload-service.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import multer from 'multer';
|
||||
import type { Options } from 'multer';
|
||||
import fs from 'fs';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const uploadService = () => {
|
||||
let folderPath = process.env.NODE_ENV == 'development' ? './src/public/' : './public/';
|
||||
if (!fs.existsSync(folderPath)) {
|
||||
fs.mkdirSync(folderPath, { recursive: true })
|
||||
}
|
||||
const { limits: templateLimits }: Options = {
|
||||
limits: {
|
||||
files: 1,
|
||||
fieldNameSize: 400,
|
||||
fileSize: 80 * 1024 * 1024,
|
||||
},
|
||||
};
|
||||
|
||||
const { filename }: multer.DiskStorageOptions = {
|
||||
filename: (_req, file, cb) => {
|
||||
const splittedFileName = file.originalname.split('.');
|
||||
let name = splittedFileName[0];
|
||||
const type = splittedFileName[1];
|
||||
name += '-' + v4().slice(0, 5) + '.' + type;
|
||||
cb(null, name);
|
||||
},
|
||||
};
|
||||
|
||||
const generateHandler = () => {
|
||||
try {
|
||||
const options: Options = {
|
||||
limits: {
|
||||
...templateLimits,
|
||||
},
|
||||
storage: multer.diskStorage({
|
||||
filename,
|
||||
destination: folderPath
|
||||
}),
|
||||
};
|
||||
|
||||
return multer(options).single('imgs');
|
||||
} catch (e) {
|
||||
console.error('Upload error', e)
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
return { generateHandler };
|
||||
};
|
8
tailwind.config.js
Normal file
8
tailwind.config.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require('daisyui')],
|
||||
}
|
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
// https://v3.nuxtjs.org/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
Loading…
Reference in a new issue