Info

Ufficio editoriale

Corso Matteotti, 5
10121 - Torino

Sede legale

Via Mosè Bianchi, 71
20149 – Milano

Servizio Clienti

+39-011.19211500

Redazione

+39-011.19211501

Email

info@webmobili.it

RRR
api fetchEvents.ts fetchEventsDetail.ts
assets
components Card.tsx Header.tsx
hooks useFetch.ts
images disco.png
pages DetailPage.tsx Homepage.tsx
utilities images.ts
App.tsx
index.css
main.tsx
types.d.ts
vite-env.d.ts



fetchEvents.ts
const URL = "https://its-events.davide-mantovani.workers.dev/events"

export const fetchEvents = async (): Promise<Event[]> => {
  try {
    const res: Response = await fetch(URL);

    if (res.ok) {
      const data = (await res.json()) as Event[];
      return data;
    } else {
      throw new Error(`Error fetching data. Status: ${res.status}`);
    }
  } catch (error) {
    console.error("Error fetching data:", error);
    return [];
  }
}



fetchEventsDetail.ts
export const fetchEventsDetail = async (id:number): Promise<EventDetail | null> => {
    const URL = `https://its-events.davide-mantovani.workers.dev/events/${id}`
  try {
    const res: Response = await fetch(URL);

    if (res.ok) {
      const data = (await res.json()) as EventDetail;
      return data;
    } else {
      throw new Error(`Error fetching data. Status: ${res.status}`);
    }
  } catch (error) {
    console.error("Error fetching data:", error);
    return null
  }
}



Card.tsx
import { Link } from "react-router-dom";

interface EventCardProps {
    event: Event;
}

const EventCard = ({ event }: EventCardProps) => {
    return (
        <div className="bg-black">
            <div className="p-5"></div>
            <div className="container mx-auto bg-black text-white">
                <div className="overflow-hidden border p-3 md:p-6">
                    <div className="flex flex-col md:flex-row items-center">
                        <div className="w-full md:w-1/2 pr-0 md:pr-8">
                            <img
                                src={event.coverImage}
                                alt="festa"
                                className="w-full h-[70%] object-cover"
                            />
                        </div>
                        <div className="w-full md:w-1/2 mt-3 md:mt-0">
                            <h2 className="text-xl md:text-2xl font-semibold mb-5">
                                {event.name}
                            </h2>
                            <p className="text-white mb-5">
                                {event.description.short}
                            </p>
                            <Link
                                to={`/events/${event.id}`}
                                className="bg-black border-white border px-4 py-2 hover:bg-purple-600 mt-3"
                            >
                                Unisciti alla festa!
                            </Link>
                        </div>
                    </div>
                </div>
            </div>
            <div className="p-5"></div>
        </div>
    );
};

export default EventCard;



Header.tsx
import images from "../utilities/images";

const Header = () => {
    return (
        <div className="fixed flex justify-between items-center p-4 bg-black text-purple-600 h-14 w-full border-b border-2-white">
            <a href={"/"}>
                <img className="w-20" src={images.disco} />
            </a>
            <div className="flex space-x-4">
                <div>Menù 1</div>
                <div>Menù 2</div>
                <div>Menù 3</div>
            </div>
        </div>
    );
};

export default Header;



useFetch.ts
import { useState } from "react";
import { fetchEvents } from "../api/fetchEvents";
import { fetchEventsDetail } from "../api/fetchEventsDetail";

const useFetch = () => {
    const [events, setEvents] = useState<Event[]>([]);
    const [event, setEvent] = useState<EventDetail | null>(null);
    const getEvents = async () => {
        const data = await fetchEvents();
        if (data) {
            setEvents(data);
        }
    };

    const getEvent = async (id: number) => {
        const data = await fetchEventsDetail(id);
        if (data) {
            setEvent(data);
        }
    };
    return { events, getEvents, getEvent, event };
};

export default useFetch;



DetailPage.tsx
import { useParams } from "react-router-dom";
import useFetch from "../hooks/useFetch";
import { useEffect, useState } from "react";
/* import { ref, set } from "firebase/database";
import { db } from "../firebase/firebase"; */

const DetailPage = () => {
    const { id } = useParams();
    const { event, getEvent } = useFetch();
    const [timeSlots, setTimeSlots] = useState<string[]>([]);
    const [booked, setBooked] = useState(false);

    useEffect(() => {
        if (id) {
            getEvent(Number(id));
        }
    }, [id]);

    useEffect(() => {
        if (event && event.date) {
            const openingTime = new Date(event.date);
            const slots: string[] = [];

            for (let i = 0; i < 6; i++) {
                const slotTime = new Date(
                    openingTime.getTime() + i * 15 * 60 * 1000
                );
                const slotTimeString = slotTime.toLocaleTimeString("en-US", {
                    hour: "2-digit",
                    minute: "2-digit",
                });
                slots.push(slotTimeString);
            }

            setTimeSlots(slots);
        }
    }, [event]);

    /* const booking = () => {
        set(ref(db, "/prenotazioni"), { name: "booking" });
    }; */

    return (
        <div>
            <div className="bg-black h-full flex flex-col items-center justify-center">
                <img
                    src={event?.coverImage}
                    alt="Festa"
                    className="w-96 object-cover rounded-md shadow-md mb-2 mt-24 border-purple-600 border-2"
                />
                <div className="w-3/5 mx-auto">
                    <h1 className="text-3xl font-bold mt-4 md:mt-12 mb-4 text-purple-600 text-center">
                        {event && event.name}
                    </h1>
                    <h2 className="text-xl md:text-2xl text-white text-center">
                        {event?.date.substring(0, 10)} alle{" "}
                        {event?.date.substring(11, 16)}
                    </h2>
                    <p className="mt-3 text-white">{event?.description.long}</p>
                    <p className="mt-7 text-purple-600 text-center">
                        {event && event.includedDrinks && (
                            <>{event.includedDrinks.join(", ")} </>
                        )}
                    </p>
                    <p className="text-white text-center mt-3">
                        {event?.includedDishes &&
                            event?.includedDishes.map((dish, index) => (
                                <div>
                                    <p key={index}>Piatto: {dish.name}</p>
                                    <p>Descrizione: {dish.description}</p>
                                    <p className="mb-4">
                                        Allergeni: {dish.allergens}
                                    </p>
                                </div>
                            ))}
                    </p>
                    <div className="p-5"></div>
                    <div className="container mx-auto bg-black text-white">
                        <div className="overflow-hidden border border-purple-600 p-3 md:p-6">
                            <div className="flex justify-between mt-3">
                                <div className="w-1/2">
                                    <h3 className="text-lg font-semibold">
                                        Aperitivo:{" "}
                                        {event?.isAperitivoIncluded
                                            ? "Sì"
                                            : "No"}
                                    </h3>
                                </div>
                                <div className="w-1/2">
                                    <h3 className="text-lg font-semibold text-right">
                                        Prezzo: €{event && event.price}
                                    </h3>
                                </div>
                            </div>
                            <div className="mt-3">
                                <h3 className="text-lg font-semibold text-center">
                                    Dresscode: {event && event.dresscode}
                                </h3>
                            </div>
                        </div>
                    </div>
                    <div className="p-5"></div>
                    <div className="container mx-auto bg-black text-white mb-4">
                        <div className="overflow-hidden border border-purple-600 p-3 md:p-6">
                            <h3 className="text-lg font-semibold text-center">
                                Orari disponibili:
                            </h3>
                            <div
                                onClick={() => setBooked(true)}
                                className="flex justify-center mt-3"
                            >
                                {timeSlots.map((slot, index) => (
                                    <div
                                        key={index}
                                        className="mr-2 border rounded-md p-2 cursor-pointer hover:bg-purple-600"
                                    >
                                        {slot}
                                    </div>
                                ))}
                            </div>
                            {booked && (
                                <h2 className="text-purple-600 text-center mt-3">
                                    Hai prenotato
                                </h2>
                            )}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default DetailPage;



HomePage.tsx
import { useEffect } from "react";
import useFetch from "../hooks/useFetch";
import images from "../utilities/images";
import EventCard from "../components/Card";

const HomePage = () => {
    const { events, getEvents } = useFetch();
    useEffect(() => {
        getEvents();
    }, []);
    return (
        <div>
            <div className="bg-black flex items-center pt-14">
                <div className="flex flex-col md:flex-row md:mt-0 mb-28">
                    <div className="w-full sm:w-1/2">
                        <img
                            className="w-full h-auto"
                            src={images.disco}
                            alt="Immagine discoteca"
                        />
                    </div>
                    <div className="w-full md:w-1/2 p-8">
                        <h1 className="text-2xl md:text-4xl font-bold md:mt-24 mb-4 text-purple-600">
                            Sweet Dance O' Mine
                        </h1>
                        <p className="text-lg md:text-xl text-white mb-20">
                            Abbraccia ogni istante e immergiti nelle emozioni
                            straordinarie che la vita ha da offrirti. Rendi
                            indimenticabili le tue notti, immergendoti in
                            atmosfere mozzafiato e partecipando alle serate più
                            entusiasmanti di sempre. Fatti notare, sii
                            protagonista di momenti indimenticabili e crea
                            ricordi che dureranno per sempre.
                        </p>
                    </div>
                </div>
            </div>

            <div className="bg-black">
                <div className=" text-purple-600">
                    <h1 className="text-3xl md:text-4xl font-bold mb-5 pl-10">
                        I nostri eventi
                    </h1>
                </div>
                {events &&
                    events.map((item) => <EventCard event={item}></EventCard>)}
            </div>
        </div>
    );
};

export default HomePage;



App.tsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import HomePage from "./pages/HomePage";
import DetailPage from "./pages/DetailPage";
import Header from "./components/Header";

function App() {
    return (
        <>
            <Header />
            <BrowserRouter>
                <Routes>
                    <Route path="/" element={<HomePage />} />
                    <Route path="/events/:id" element={<DetailPage />} />
                </Routes>
            </BrowserRouter>
        </>
    );
}

export default App;



index.css
@tailwind base;
@tailwind components;
@tailwind utilities;



main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);



types.d.ts
interface Event {
    id: number;
    name: string;
    coverImage: string;
    date: string;
    description: Description;
    dresscode: string;
    price: number;
    includedDrinks: string[];
    tags: string[];
    isAperitivoIncluded: boolean;
}

interface Description {
    short: string;
    long?: string[];
}

interface IncludedDish {
    name: string;
    description: string;
    allergens: string[];
}

interface EventDetail extends Event {
    includedDishes?: IncludedDish[];
}



vite-env.d.ts
/// <reference types="vite/client" />

POKE
api getDetail.ts getPokemon.ts
assets react.svg
components detail.tsx
types pokemonData.type.ts typeDetail.ts
App.css
App.txt
index.css
main.tsx
vite.env.d.ts



getDetail.ts
import { PokemonDetail } from "../types/typeDetail";

const URL = "https://pokeapi.co/api/v2/";

export const getPokemonDetail = async (
    detailId: string
): Promise<PokemonDetail> => {
    const res = await fetch(URL + `pokemon/${detailId}`);
    const json = await res.json();
    return json;
};



getPokemon.ts
import { PokemonData } from "../types/pokemonData.type";

const URL = "https://pokeapi.co/api/v2/";

export const getPokemon = async (): Promise<PokemonData> => {
    const res = await fetch(URL + "pokemon");
    const json = await res.json();
    return json;
};



detail.tsx
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { getPokemonDetail } from "../api/getDetail";
import { PokemonDetail } from "../types/typeDetail";

const Detail = () => {
    const { detailId } = useParams();
    const [pokemonData, setPokemonData] = useState<PokemonDetail | null>(null);

    useEffect(() => {
        const fetchData = async () => {
            if (!detailId) {
                return;
            }
            const data = await getPokemonDetail(detailId);
            setPokemonData(data);
        };

        fetchData();
    }, [detailId]);
    return (
        <div className="text-white bg-black min-h-screen">
            <div>
                <div className="text-center text-2xl">{detailId} </div>

                <div>{pokemonData?.height}</div>
                <div>{pokemonData?.weight}</div>
                <div className="text-center text-7xl whitespace-nowrap flex items-center space-x-5 justify-center">
                    <p>La pagina di</p>
                    {pokemonData?.forms.map((item) => (
                        <div>{item.name}</div>
                    ))}
                </div>
                <div className="flex justify-center">
                    <img
                        src={pokemonData?.sprites.front_default}
                        alt=""
                        className="h-[300px]"
                    />
                </div>
                <div className="grid grid-cols-4 gap-4 w-full">
                    <div className="bg-yellow-400 text-center text-black">
                        <div className="text-xl">
                            <strong>Abilità</strong>
                        </div>
                        <div>
                            {pokemonData?.abilities.map((item) => (
                                <div>{item.ability.name}</div>
                            ))}
                        </div>
                    </div>
                </div>
                <div>
                    {pokemonData?.stats.map((item) => (
                        <div>{item.base_stat}</div>
                    ))}
                </div>
                <div>
                    {pokemonData?.stats.map((item) => (
                        <div>{item.stat.name}</div>
                    ))}
                </div>
            </div>
        </div>
    );
};

export default Detail;



pokemonData.type.ts
export interface PokemonData {
    count: number;
    next: string | null;
    previous: string | null;
    results: PokemonResults[];
}

interface PokemonResults {
    name: string;
    url: string;
}



typeDetail.ts
export interface PokemonDetail {
    abilities: Ability[];
    base_experience: number;
    cries: Cries;
    forms: Species[];
    game_indices: GameIndex[];
    height: number;
    held_items: any[];
    id: number;
    is_default: boolean;
    location_area_encounters: string;
    moves: Move[];
    name: string;
    order: number;
    past_abilities: any[];
    past_types: any[];
    species: Species;
    sprites: Sprites;
    stats: Stat[];
    types: Type[];
    weight: number;
}

export interface Ability {
    ability: Species;
    is_hidden: boolean;
    slot: number;
}

export interface Species {
    name: string;
    url: string;
}

export interface Cries {
    latest: string;
    legacy: string;
}

export interface GameIndex {
    game_index: number;
    version: Species;
}

export interface Move {
    move: Species;
    version_group_details: VersionGroupDetail[];
}

export interface VersionGroupDetail {
    level_learned_at: number;
    move_learn_method: Species;
    version_group: Species;
}

export interface GenerationV {
    "black-white": Sprites;
}

export interface GenerationIv {
    "diamond-pearl": Sprites;
    "heartgold-soulsilver": Sprites;
    platinum: Sprites;
}

export interface Versions {
    "generation-i": GenerationI;
    "generation-ii": GenerationIi;
    "generation-iii": GenerationIii;
    "generation-iv": GenerationIv;
    "generation-v": GenerationV;
    "generation-vi": { [key: string]: Home };
    "generation-vii": GenerationVii;
    "generation-viii": GenerationViii;
}

export interface Other {
    dream_world: DreamWorld;
    home: Home;
    "official-artwork": OfficialArtwork;
    showdown: Sprites;
}

export interface Sprites {
    back_default: string;
    back_female: null;
    back_shiny: string;
    back_shiny_female: null;
    front_default: string;
    front_female: null;
    front_shiny: string;
    front_shiny_female: null;
    other?: Other;
    versions?: Versions;
    animated?: Sprites;
}

export interface GenerationI {
    "red-blue": RedBlue;
    yellow: RedBlue;
}

export interface RedBlue {
    back_default: string;
    back_gray: string;
    back_transparent: string;
    front_default: string;
    front_gray: string;
    front_transparent: string;
}

export interface GenerationIi {
    crystal: Crystal;
    gold: Gold;
    silver: Gold;
}

export interface Crystal {
    back_default: string;
    back_shiny: string;
    back_shiny_transparent: string;
    back_transparent: string;
    front_default: string;
    front_shiny: string;
    front_shiny_transparent: string;
    front_transparent: string;
}

export interface Gold {
    back_default: string;
    back_shiny: string;
    front_default: string;
    front_shiny: string;
    front_transparent?: string;
}

export interface GenerationIii {
    emerald: OfficialArtwork;
    "firered-leafgreen": Gold;
    "ruby-sapphire": Gold;
}

export interface OfficialArtwork {
    front_default: string;
    front_shiny: string;
}

export interface Home {
    front_default: string;
    front_female: null;
    front_shiny: string;
    front_shiny_female: null;
}

export interface GenerationVii {
    icons: DreamWorld;
    "ultra-sun-ultra-moon": Home;
}

export interface DreamWorld {
    front_default: string;
    front_female: null;
}

export interface GenerationViii {
    icons: DreamWorld;
}

export interface Stat {
    base_stat: number;
    effort: number;
    stat: Species;
}

export interface Type {
    slot: number;
    type: Species;
}



app.tsx
import { useEffect, useState } from "react";
import { getPokemon } from "./api/getPokemon";
import { PokemonData } from "./types/pokemonData.type";
import { Link } from "react-router-dom";

function App() {
    const [pokemonData, setPokemonData] = useState<PokemonData | null>(null);

    useEffect(() => {
        const fetchData = async () => {
            const data = await getPokemon();
            setPokemonData(data);
        };

        fetchData();
    }, []);
    return (
        <div className="min-h-screen w-full">
            <div className="w-1/3 mx-auto space-y-20 pt-20">
                <h1 className="text-center">POKEMON!!!</h1>
                <div className="grid grid-cols-2 gap-4 w-full">
                    {pokemonData?.results.map((pokemon) => (
                        <Link
                            to={`/detail/${pokemon.name}`}
                            key={pokemon.name}
                            className="h-64 shadow-md rounded-lg w-full flex items-center justify-center bg-red-600"
                        >
                            {pokemon.name}
                        </Link>
                    ))}
                </div>
            </div>
        </div>
    );
}

export default App;



index.css
@tailwind base;
@tailwind components;
@tailwind utilities;



main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import Detail from "./components/detail.tsx";

const router = createBrowserRouter([
    {
        path: "/",
        element: <App></App>,
        // children: [
        //     {
        //         path: "detail/:detailId",
        //         element: <Detail />,
        //     },
        // ],
    },
    {
        path: "detail/:detailId",
        element: <Detail />,
    },
]);

ReactDOM.createRoot(document.getElementById("root")!).render(
    <React.StrictMode>
        <RouterProvider router={router} />
    </React.StrictMode>
);



tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

AAAA
app
detail detail.component.css detail.component.html detail.component.ts
home-page home-page.component.css home-page.component.html home-page.component.ts
page-404 page-404.component.css page-404.component.html page-404.component.ts
api.call.ts
app-routing.module.ts
app.component.css
app.component.html
app.component.ts
app.module.ts
products.ts
./app
assets shipping.json
index.html
main.ts
styles.css



detail.component.css
.container {
    font-size: 30px;
}

.bg-blue {
    background-color: rgb(48, 0, 126);
}



detail.component.html
<div *ngIf="dati" class="mt-5">
    <h1>Previsioni di oggi</h1>
    <div class="container d-flex flex-wrap">
        <div class="container m-5">
            <div *ngIf="sole">
                <div class="row">
                    <div class="col-md-6 text-center text-nowrap">
                        <img src="https://cdn.icon-icons.com/icons2/1370/PNG/512/if-weather-27-2682824_90788.png" alt="alba" height="50px" width="50px">
                        {{ sole.results.sunrise }}
                    </div>
                    <div class="col-md-3 text-center text-nowrap ms-3">
                        <img src="https://cdn.icon-icons.com/icons2/1370/PNG/512/if-weather-26-2682825_90789.png" alt="tramonto" height="50px" width="50px">
                        {{ sole.results.sunset }}
                    </div>
                </div>
            </div>
        </div>

        <div class="row mb-4 w-100">
            <div class="col-md-2"></div>
            <div class="col-md-1">
                <img src="https://static-00.iconduck.com/assets.00/clock-icon-2048x2048-org46qq3.png" alt="orario" height="50px" width="50px">
            </div>
            <div class="col-md-1">
                <img src="https://cdn-icons-png.flaticon.com/512/3767/3767036.png" alt="meteo" height="50px" width="50px">
            </div>
            <div class="col-md-2">
                <img src="https://static.vecteezy.com/system/resources/previews/019/860/394/original/thermometer-icon-colorful-free-png.png" alt="temperatura" height="50px" width="50px">
            </div>
            <div class="col-md-1">
                <img src="https://cdn0.iconfinder.com/data/icons/weather-406/24/visibility-sight-eye-distance-weather-512.png" alt="visibilità" height="50px" width="50px">
            </div>
            <div class="col-md-1">
                <img src="https://cdn-icons-png.flaticon.com/512/4535/4535075.png" alt="vento" height="50px" width="50px">
            </div>
            <div class="col-md-2 ms-3">
                <img src="https://cdn-icons-png.flaticon.com/512/3226/3226468.png" alt="velocità-vento" height="50px" width="50px">
            </div>
        </div>

        <div class="row mb-4 w-100" *ngFor="let parametro of dati.dataseries">
            <div class="col-md-2"></div>
            <div class="col-md-1 ms-3">
                {{ parametro.timepoint }}
            </div>
            <div class="col-md-1">
                {{ parametro.cloudcover }}
            </div>
            <div class="col-md-2">
                {{ parametro.temp2m }}
            </div>
            <div class="col-md-1">
                {{ parametro.transparency }}
            </div>
            <div class="col-md-1">
                {{ parametro.wind10m.speed }}
            </div>
            <div class="col-md-2">
                {{ parametro.wind10m.direction }}
            </div>
            <div class="col-md-3"></div>
            <div class="bg-blue col-md-6" style="height: 3.5px;"></div>
        </div>
    </div>
</div>



detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ApiService } from '../api.call';


@Component({
  selector: 'Detail',
  templateUrl: './detail.component.html',
  styleUrls: ['./detail.component.css']
})
export class Detail implements OnInit {
  dati: any;
  sole: any;
  lat: string = "";
  long: string = "";

  constructor(private apiService: ApiService, private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.queryParams.subscribe((results) => {this.lat = results["lat"]})
    this.route.queryParams.subscribe((results) => {this.long = results["long"]})
    console.log(this.lat);
    console.log(this.long);
    this.apiService.getDati(this.lat, this.long).subscribe((dati) => {this.dati = dati;
    console.log(this.dati)})
    this.apiService.getSole(this.lat, this.long).subscribe((dati) => {this.sole = dati;
    console.log(this.sole)})
  }
  };
 


home-page.component.css
a {
    font-size: 26px;
    color: black;
}

button {
    background-color: rgb(48, 0, 126);
    color: aliceblue;
}

button:hover {
    background-color: rgb(77, 103, 255);
    color: aliceblue;
}

a {
    text-align: center;
}

a:hover {
    background-color: rgb(48, 0, 126);
    color: aliceblue;
    border-radius: 12px;
}



home-page.component.html
<div class="mt-5">
    <h1 class="mb-3">MeteoNow</h1>
    <h2 class="mb-5">Tutte le previsioni, in ogni angolo del mondo</h2>
    <div class="container d-flex justify-content-center">
        <div class="form-group row">
            <div class="col-md-6 mb-2">
                <input type="text" class="form-control" placeholder="Latitudine" [(ngModel)]="lat">
            </div>
            <div class="col-md-6">
                <input type="text" class="form-control" placeholder="Longitudine" [(ngModel)]="long">
            </div>
            <div class="form-group mb-4">
                <button class="btn" style="width: 100%;" (click)="save()">Scopri</button>
            </div>
        </div>
    </div>

    <div class="container d-flex justify-content-center mt-5">
        <div class="row w-100">
            <div class="d-flex flex-column mt-3 col-md-2">
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 45.07, long: 7.68 }">Torino</a>
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 40.78, long: -73.97 }">New York</a>
            </div>
            <div class="d-flex flex-column mt-3 col-md-2">
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 41.90, long: 12.49 }">Roma</a>
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 48.85, long: 2.35 }">Parigi</a>
            </div>
            <div class="d-flex flex-column mt-3 col-md-2">
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 25.20, long: 55.27 }">Dubai</a>
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 39.90, long: 116.40 }">Pechino</a>
            </div>
            <div class="d-flex flex-column mt-3 col-md-2">
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 59.91, long: 10.75 }">Oslo</a>
                <a [routerLink]="['/detail']" [queryParams]="{ lat: -33.85, long: 151.23 }">Sydney</a>
            </div>
            <div class="d-flex flex-column mt-3 col-md-2">
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 38.72, long: -9.13 }">Lisbona</a>
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 35.65, long: 139.83 }">Tokyo</a>
            </div>
            <div class="d-flex flex-column mt-3 col-md-2">
                <a [routerLink]="['/detail']" [queryParams]="{ lat: 47.49, long: 19.04 }">Budapest</a>
                <a [routerLink]="['/detail']" [queryParams]="{ lat: -6.20, long: 39.36 }">Zanzibar</a>
            </div>
        </div>
    </div>
</div>



home-page.component.ts
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../api.call';
import { Router } from '@angular/router';


@Component({
  selector: 'HomePage',
  templateUrl: './home-page.component.html',
  styleUrls: ['./home-page.component.css']
})
export class HomePage implements OnInit {
  dati: any;
  lat: string = "";
  long: string = "";

  constructor(private apiService: ApiService, private router: Router) {}

  ngOnInit() {
       
    };

  save() {
    console.log(this.lat)
    console.log(this.long)
    this.router.navigate(['/detail'], {
        queryParams: { lat: this.lat, lon: this.long },
      });
  };
  }



page-404.component.css vuota

page-404.component.html ​<h1>ERROR 404</h1>

page-404.component.ts
import { Component } from '@angular/core';


@Component({
  selector: 'page-404',
  templateUrl: './page-404.component.html',
  styleUrls: ['./page-404.component.css']
})
export class Page404 {
}



api.call.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})

export class ApiService {

  constructor(private http: HttpClient) {}

  getDati(lat:string, long:string) {
    return this.http.get(`https://www.7timer.info/bin/astro.php?lon=${long}&lat=${lat}&ac=0&unit=metric&output=json&tzshift=0`);
  }

  getSole(lat:string, long:string) {
    return this.http.get(`https://api.sunrisesunset.io/json?lat=${lat}&lng=${long}`);
  }
}



app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
import { HomePage } from './home-page/home-page.component';
import { Page404 } from './page-404/page-404.component';
import { Detail } from './detail/detail.component';

const routes: Routes = [
  { path: 'home', component: HomePage },
  { path: 'detail', component: Detail },
  { path: '',   redirectTo: 'home', pathMatch: 'full' },
  { path: '**', component: Page404 },  
];// sets up routes constant where you define your routes

// configures NgModule imports and exports
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }



app.component.css vuoto

app.component.html
<nav class="navbar navbar-expand-lg bg-dark fixed-top" data-bs-theme="dark" id="navbar">
    <div class="container">
        <a class="navbar-brand" routerLink="/">
            <img src="https://i.ibb.co/jwgBVT0/meteo.png" alt="Logo" height="40" width="40">
        </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto d-flex ms-5" style="width: 100%;">
                <li class="nav-item">
                    <a class="nav-link active" routerLink="/">Home</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link active" href="#">Chi siamo</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link active" href="#">Contatti</a>
                </li>
            </ul>
        </div>
    </div>
</nav>


<router-outlet></router-outlet>



app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

}



app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HomePage } from './home-page/home-page.component';
import { Detail } from './detail/detail.component';
import { AppRoutingModule } from './app-routing.module';
import { Page404 } from './page-404/page-404.component';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  declarations: [
    AppComponent,
    HomePage,
    Page404,
    Detail,
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }



products.ts
export interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
}

export const products = [
  {
    id: 1,
    name: 'Phone XL',
    price: 799,
    description: 'A large phone with one of the best screens'
  },
  {
    id: 2,
    name: 'Phone Mini',
    price: 699,
    description: 'A great phone with one of the best cameras'
  },
  {
    id: 3,
    name: 'Phone Standard',
    price: 299,
    description: ''
  }
];



shipping.json
[
  {
    "type": "Overnight",
    "price": 25.99
  },
  {
    "type": "2-Day",
    "price": 9.99
  },
  {
    "type": "Postal",
    "price": 2.99
  }
]



index.html
<!DOCTYPE html>
<html lang="it">

<head>
    <meta charset="utf-8" />
    <title>MeteoNow</title>
    <base href="/" />

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="https://i.ibb.co/jwgBVT0/meteo.png" />
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
</head>

<body>
    <app-root>
        <h2>loading...</h2>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
    </app-root>
</body>

</html>



main.ts
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));



styles.css
/* Global Styles */

body {
    background-image: linear-gradient(
        to left top,
        #eaf2ff,
        #e7f0fe,
        #e4edfe,
        #e0ebfd,
        #dde9fc
    );
}

* {
    font-family: "Roboto", Arial, sans-serif;
    color: black;
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    margin: 0;
}

.container {
    display: flex;
    flex-direction: row;
}

router-outlet + * {
    padding: 0 16px;
}

.card {
    background-color: rgb(225, 224, 201);
}

/* Text */

h1 {
    padding-top: 30px;
    padding-bottom: 20px;
    font-size: 60px;
    -webkit-text-stroke: 0.8px rgb(255, 255, 255);
    font-weight: bold;
    text-align: center;
}

h2 {
    font-size: 35px;
    -webkit-text-stroke: 0.4px black;
    text-align: center;
}

p {
    font-size: 14px;
}

/* Hyperlink */

a {
    cursor: pointer;
    text-decoration: none;
}

a:hover {
    opacity: 1;
}

/* Input */

input {
    font-size: 14px;
    border-radius: 2px;
    padding: 8px;
    margin-bottom: 16px;
    border: 1px solid #bdbdbd;
}

label {
    font-size: 12px;
    font-weight: bold;
    margin-bottom: 4px;
    display: block;
    text-transform: uppercase;
}

/* Button */
.button,
button {
    display: inline-flex;
    align-items: center;
    padding: 8px 16px;
    border-radius: 2px;
    font-size: 14px;
    cursor: pointer;
    background-color: #1976d2;
    color: white;
    border: none;
}

.button:hover,
button:hover {
    opacity: 0.8;
    font-weight: normal;
}

.button:disabled,
button:disabled {
    opacity: 0.5;
    cursor: auto;
}

/* Fancy Button */

.fancy-button {
    background-color: white;
    color: #1976d2;
}

.fancy-button i.material-icons {
    color: #1976d2;
    padding-right: 4px;
}

/* Top Bar */

app-top-bar {
    width: 100%;
    height: 68px;
    background-color: #1976d2;
    padding: 16px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
}

app-top-bar h1 {
    color: white;
    margin: 0;
}

/* Checkout Cart, Shipping Prices */

.cart-item,
.shipping-item {
    width: 100%;
    min-width: 400px;
    max-width: 450px;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    padding: 16px 32px;
    margin-bottom: 8px;
    border-radius: 2px;
    background-color: #eeeeee;
}

/*
Copyright Google LLC. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at https://angular.io/license
*/

EEE
assets react.svg
pages Detail.tsx HomePage.tsx
types detail.type.ts prodotti.type.ts
App.css
App.tsx
index.css
main.tsx
router.tsx



Detail.tsx
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { DetailType } from "../types/detail.type";

const Detail = () => {
    const { id } = useParams();
    const [detail, setDetail] = useState<DetailType | null>(null);
    const getDetail = async (): Promise<DetailType> => {
        const res = await fetch(`https://dummyjson.com/products/${id}`);
        const data = await res.json();
        console.log(data);
        setDetail(data);
        return data;
    };
    useEffect(() => {
        getDetail();
    }, []);
    return (
        <div>
            {detail?.id}
            {detail?.title}
            <img src={detail?.thumbnail} alt="" />
        </div>
    );
};

export default Detail;



HomePage.tsx
import React, { useEffect, useState } from "react";
import { Prodotti } from "../types/prodotti.type";
import { Link } from "react-router-dom";

const HomePage = () => {
    const [dataProduct, setDataProduct] = useState<Prodotti | null>(null);
    const getProduct = async (): Promise<Prodotti> => {
        const res = await fetch("https://dummyjson.com/products");
        const data = await res.json();
        console.log(data);
        setDataProduct(data);
        return data;
    };
    useEffect(() => {
        getProduct();
    }, []);
    return (
        <div>
            <div className="flex gap-5 flex-wrap">
                {dataProduct?.products.map((nome) => (
                    <div className="bg-red-400 p-5">
                        {nome.title}
                        <img src={nome.thumbnail} alt="" />
                        <Link to={`detail/${nome.id}`}> vai </Link>
                    </div>
                ))}
            </div>
        </div>
    );
};

export default HomePage;



detail.type.ts
export interface DetailType {
    id: number;
    title: string;
    description: string;
    category: string;
    price: number;
    discountPercentage: number;
    rating: number;
    stock: number;
    tags: string[];
    brand: string;
    sku: string;
    weight: number;
    dimensions: Dimensions;
    warrantyInformation: string;
    shippingInformation: string;
    availabilityStatus: string;
    reviews: Review[];
    returnPolicy: string;
    minimumOrderQuantity: number;
    meta: Meta;
    images: string[];
    thumbnail: string;
}

export interface Dimensions {
    width: number;
    height: number;
    depth: number;
}

export interface Meta {
    createdAt: Date;
    updatedAt: Date;
    barcode: string;
    qrCode: string;
}

export interface Review {
    rating: number;
    comment: string;
    date: Date;
    reviewerName: string;
    reviewerEmail: string;
}



prodotti.type.ts
export interface Prodotti {
    products: Product[];
    total: number;
    skip: number;
    limit: number;
}

export interface Product {
    id: number;
    title: string;
    description: string;
    category: Category;
    price: number;
    discountPercentage: number;
    rating: number;
    stock: number;
    tags: string[];
    brand?: string;
    sku: string;
    weight: number;
    dimensions: Dimensions;
    warrantyInformation: string;
    shippingInformation: string;
    availabilityStatus: AvailabilityStatus;
    reviews: Review[];
    returnPolicy: ReturnPolicy;
    minimumOrderQuantity: number;
    meta: Meta;
    images: string[];
    thumbnail: string;
}

export enum AvailabilityStatus {
    InStock = "In Stock",
    LowStock = "Low Stock",
}

export enum Category {
    Beauty = "beauty",
    Fragrances = "fragrances",
    Furniture = "furniture",
    Groceries = "groceries",
}

export interface Dimensions {
    width: number;
    height: number;
    depth: number;
}

export interface Meta {
    createdAt: Date;
    updatedAt: Date;
    barcode: string;
    qrCode: string;
}

export enum ReturnPolicy {
    NoReturnPolicy = "No return policy",
    The30DaysReturnPolicy = "30 days return policy",
    The60DaysReturnPolicy = "60 days return policy",
    The7DaysReturnPolicy = "7 days return policy",
    The90DaysReturnPolicy = "90 days return policy",
}

export interface Review {
    rating: number;
    comment: string;
    date: Date;
    reviewerName: string;
    reviewerEmail: string;
}



App.tsx
import "./App.css";
import { RouterProvider } from "react-router-dom";
import router from "./router";

function App() {
    return <RouterProvider router={router} />;
}

export default App;



index.css
@tailwind base;
@tailwind components;
@tailwind utilities;



main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)



router.tsx
import { createBrowserRouter } from "react-router-dom";
import HomePage from "./pages/HomePage";
import Detail from "./pages/Detail";

const router = createBrowserRouter([
    {
        path: "/",
        element: <HomePage />,
    },
    {
        path: "/detail/:id",
        element: <Detail />,
    },
]);
export default router;

PPP
db​ connessione.php
index.php
inserisci.php
resoconto.php
second_form.php
validazione.php



connessione.php
<?php
class Connessione
{
    private $host = 'localhost:3306';
    private $db = 'bollettini';
    private $user = 'alephp';
    private $pass = 'alephp';
    private $charset = 'utf8mb4';
    private $conn;

    public function connect()
    {
        $dsn = "mysql:host=$this->host;dbname=$this->db;charset=$this->charset";
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ];

        try {
            $this->conn = new PDO($dsn, $this->user, $this->pass, $options);
        } catch (\PDOException $e) {
            throw new \PDOException($e->getMessage(), (int) $e->getCode());
        }

        return $this->conn;
    }
}

?>



index.php
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <form method="post" action="validazione.php">
        Conto Corrente: <input type="text" name="conto_corrente"><br>
        Importo: <input type="text" name="importo"><br>
        Causale: <input type="text" name="causale"><br>
        Carta credito: <input type="text" name="carta_credito"><br>
        <input type="submit" value="Submit">
    </form>
</body>

</html>



inserisci.php
<?php
session_start();
require './db/connessione.php';

$db = new Connessione();
$conn = $db->connect();

$conto_corrente = $_SESSION['conto_corrente'];
$importo = $_SESSION['importo'];
$causale = $_SESSION['causale'];
$carta_credito = $_SESSION['carta_credito'];
$nome = $_POST['nome'];
$cognome = $_POST['cognome'];

$stmt = $conn->prepare("INSERT INTO bollettino (conto_corrente, causale, importo, nome, cognome, carta_credito) VALUES (:conto_corrente, :causale, :importo, :nome, :cognome, :carta_credito)");
$stmt->execute([':conto_corrente' => $conto_corrente, ':causale' => $causale, ':importo' => $importo, ':nome' => $nome, ':cognome' => $cognome, ':carta_credito' => $carta_credito]);

// Prendere l'ID dell'ultimo inserimento
$codice_bollettino = $conn->lastInsertId();
// Salvare il codice bollettino nella sessione
$_SESSION['codice_bollettino'] = $codice_bollettino;
header('Location: resoconto.php');
exit();
?>



resoconto.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<?php
session_start();
require './db/connessione.php';

$db = new Connessione();
$conn = $db->connect();

$stmt = $conn->prepare("SELECT * FROM bollettino WHERE conto_corrente = :conto_corrente AND codice_bollettino = :codice_bollettino");
$stmt->execute([':conto_corrente' => $_SESSION['conto_corrente'], ':codice_bollettino' => $_SESSION['codice_bollettino']]);
$user = $stmt->fetch();

echo '<h1>RESOCONTO</h1>';
echo 'Nome: ' . $user['nome'] . '<br>';
echo 'Cognome: ' . $user['cognome'] . '<br>';
echo 'Conto Corrente: ' . $user['conto_corrente'] . '<br>';
echo 'Codice Bollettino: ' . $user['codice_bollettino'] . '<br>';
echo 'Importo: ' . $user['importo'] . '<br>';
echo 'Causale: ' . $user['causale'] . '<br>';
echo 'Carta di credito: ' . $user['carta_credito'] . '<br>';


if(session_status() == PHP_SESSION_ACTIVE) {
    session_destroy();
}
?>

<a href="/esame-galliano">Esegui un altro pagamento</a>

</body>
</html>



second_form.php
<!DOCTYPE html>
<html>
<body>
<form method="post" action="inserisci.php">
    Nome: <input type="text" name="nome"><br>
    Cognome: <input type="text" name="cognome"><br>
    <input type="submit" value="Submit">
</form>
</body>
</html>



validazione.php
<?php
session_start();

require './db/connessione.php';

$db = new Connessione();
$conn = $db->connect();

$conto_corrente = $_POST['conto_corrente'];
$causale = $_POST['causale'];
$importo = $_POST['importo'];
$carta_credito = $_POST['carta_credito'];

$stmt = $conn->prepare("SELECT * FROM conto_corrente WHERE codice_conto = :conto_corrente");
$stmt->execute([':conto_corrente' => $conto_corrente]);
$user = $stmt->fetch();

if(!$user) {
    echo 'Conto corrente non esiste. Per favore, inserisci un conto corrente valido.';
} else {
    $_SESSION['conto_corrente'] = $conto_corrente;
    $_SESSION['importo'] = $importo;
    $_SESSION['causale'] = $causale;
    $_SESSION['carta_credito'] = $carta_credito;
    header('Location: second_form.php');
    exit();
}
?>

to L

# Readme

## Installazione

Dal terminale installare React con vite

```bash
npm create vite@latest
```
Installare la versione base con typescript.

Per installare tailwind dal terminale digitare il seguente comando:

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

N.B. Devi essere nella root del progetto.

Modificare il tailwind.config.js come segue:

```js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
```

Modificare index.css come segue

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Per l'installazione di react router digitare il seguente comando

```bash
npm install react-router-dom
```
## Configurazione iniziale

Cancellare il contenuto di App.tsx così da iniziare la nostra applicazione nuova.
Creare la cartella pages che conterrà i nostri componenti che rappresentano le pagine della web application.
Dentro pages creare HomePage.tsx e Detail.tsx.
Creare un file router.tsx all'interno della cartella src.
Creare una cartella types con all'interno due file (es. pippo.type.ts)
In router.tsx configuare le rotte come nell'esempio.

```js
import { createBrowserRouter, useParams } from "react-router-dom";
import Detail from "./pages/Detail";
import HomePage from "./pages/HomePage";

const router = createBrowserRouter([
{
path:"/",
element:<HomePage/>
},
{
path : '/detail/:id',
element : <Detail/>
}
])
export default router
```

In App.tsx configurare il router

```js
function App() {
return (
<RouterProvider router={router}/>
)
}

export default App
```

## Creare i tipi
Studiare la risposta API e creare i tipi in base ai valori che ritorna. Quicktype può venire incontro.

## Come effettuare una chiamata API

```js
import React, { useEffect, useState } from 'react'
import { ProductResponse } from '../types/productRespons.type'

const HomePage = () => {
const [dataProduct,setDataProduct] = useState<ProductResponse|null>(null)
const getProduct = async ():Promise<ProductResponse> => {
const res = await fetch('https://dummyjson.com/products')
const data = await res.json()
console.log(data)
setDataProduct(data)
return data
}
useEffect(()=>{
getProduct()

},[])
return (
<div>
<div>
{dataProduct?.products.map((nome)=>(
nome.brand
))}
</div>
</div>
)
}

export default HomePage

CAMBIARE ProductResponse con il nome della prima parte del file del tipo con la prima lettera maiuscola (es. Pippo)
Nell'import tenere la lettera minuscola

```
## Pagina del dettaglio
```js
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { ProductResponseDetail } from '../types/productResponseDetail.type'

const Detail = () => {
const {id} = useParams()
const [detail, setDetail] = useState<ProductResponseDetail|null>(null)
const getDetail = async ():Promise<ProductResponseDetail> =>{
const res = await fetch(https://dummyjson.com/products/${id})
const data = await res.json()
console.log(data)
setDetail(data)
return data
}
useEffect(()=>{
getDetail()

},[])
return (
<div>
{detail?.id}
{detail?.title}
<img src={detail?.thumbnail} alt="" />
</div>
)
}

export default Detail
```

Per far funzionare tutto cancellare import HomePage e import Detail riscrivendoli da capo

Auto Rename Tag
Beautify
Comment Divider
ES7+ React/Redux/React-Native snippets 
Highlight Matching Tag
HTML CSS Support
Italian Language Pack for Visual Studio Code
Prettier - Code formatter
Simple React Snippets
Tailwind CSS IntelliSense