featmigrate from Angular to Astro static site
BREAKING CHANGE: Complete rewrite from Angular SPA to Astro - Replace Angular 18 with Astro 5.16.7 + Tailwind CSS - Convert all Angular components to Astro components - Add content collections for blog with markdown support - Setup S3 deployment with CloudFront invalidation - Add RSS feed and sitemap generation - Configure Prettier and Biome for code formatting - Switch from npm to pnpm - Remove Amplify backend (now fully static) - Improve SEO and performance with static generation
25
.astro/collections/bio.schema.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/bio",
|
||||||
|
"definitions": {
|
||||||
|
"bio": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"$schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"title",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
|
}
|
||||||
76
.astro/collections/blog.schema.json
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/blog",
|
||||||
|
"definitions": {
|
||||||
|
"blog": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pubDate": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "unix-time"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"updatedDate": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "unix-time"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"heroImage": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "Lorenzo Iovino"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"draft": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"$schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"pubDate"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
|
}
|
||||||
1
.astro/content-assets.mjs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export default new Map();
|
||||||
1
.astro/content-modules.mjs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export default new Map();
|
||||||
219
.astro/content.d.ts
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
declare module 'astro:content' {
|
||||||
|
export interface RenderResult {
|
||||||
|
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
|
||||||
|
headings: import('astro').MarkdownHeading[];
|
||||||
|
remarkPluginFrontmatter: Record<string, any>;
|
||||||
|
}
|
||||||
|
interface Render {
|
||||||
|
'.md': Promise<RenderResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RenderedContent {
|
||||||
|
html: string;
|
||||||
|
metadata?: {
|
||||||
|
imagePaths: Array<string>;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'astro:content' {
|
||||||
|
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
||||||
|
|
||||||
|
export type CollectionKey = keyof AnyEntryMap;
|
||||||
|
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
|
||||||
|
|
||||||
|
export type ContentCollectionKey = keyof ContentEntryMap;
|
||||||
|
export type DataCollectionKey = keyof DataEntryMap;
|
||||||
|
|
||||||
|
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
||||||
|
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
|
||||||
|
ContentEntryMap[C]
|
||||||
|
>['slug'];
|
||||||
|
|
||||||
|
export type ReferenceDataEntry<
|
||||||
|
C extends CollectionKey,
|
||||||
|
E extends keyof DataEntryMap[C] = string,
|
||||||
|
> = {
|
||||||
|
collection: C;
|
||||||
|
id: E;
|
||||||
|
};
|
||||||
|
export type ReferenceContentEntry<
|
||||||
|
C extends keyof ContentEntryMap,
|
||||||
|
E extends ValidContentEntrySlug<C> | (string & {}) = string,
|
||||||
|
> = {
|
||||||
|
collection: C;
|
||||||
|
slug: E;
|
||||||
|
};
|
||||||
|
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
||||||
|
collection: C;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @deprecated Use `getEntry` instead. */
|
||||||
|
export function getEntryBySlug<
|
||||||
|
C extends keyof ContentEntryMap,
|
||||||
|
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||||
|
>(
|
||||||
|
collection: C,
|
||||||
|
// Note that this has to accept a regular string too, for SSR
|
||||||
|
entrySlug: E,
|
||||||
|
): E extends ValidContentEntrySlug<C>
|
||||||
|
? Promise<CollectionEntry<C>>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
|
||||||
|
/** @deprecated Use `getEntry` instead. */
|
||||||
|
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
|
||||||
|
collection: C,
|
||||||
|
entryId: E,
|
||||||
|
): Promise<CollectionEntry<C>>;
|
||||||
|
|
||||||
|
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
|
||||||
|
collection: C,
|
||||||
|
filter?: (entry: CollectionEntry<C>) => entry is E,
|
||||||
|
): Promise<E[]>;
|
||||||
|
export function getCollection<C extends keyof AnyEntryMap>(
|
||||||
|
collection: C,
|
||||||
|
filter?: (entry: CollectionEntry<C>) => unknown,
|
||||||
|
): Promise<CollectionEntry<C>[]>;
|
||||||
|
|
||||||
|
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
|
||||||
|
collection: C,
|
||||||
|
filter?: LiveLoaderCollectionFilterType<C>,
|
||||||
|
): Promise<
|
||||||
|
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function getEntry<
|
||||||
|
C extends keyof ContentEntryMap,
|
||||||
|
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||||
|
>(
|
||||||
|
entry: ReferenceContentEntry<C, E>,
|
||||||
|
): E extends ValidContentEntrySlug<C>
|
||||||
|
? Promise<CollectionEntry<C>>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
export function getEntry<
|
||||||
|
C extends keyof DataEntryMap,
|
||||||
|
E extends keyof DataEntryMap[C] | (string & {}),
|
||||||
|
>(
|
||||||
|
entry: ReferenceDataEntry<C, E>,
|
||||||
|
): E extends keyof DataEntryMap[C]
|
||||||
|
? Promise<DataEntryMap[C][E]>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
export function getEntry<
|
||||||
|
C extends keyof ContentEntryMap,
|
||||||
|
E extends ValidContentEntrySlug<C> | (string & {}),
|
||||||
|
>(
|
||||||
|
collection: C,
|
||||||
|
slug: E,
|
||||||
|
): E extends ValidContentEntrySlug<C>
|
||||||
|
? Promise<CollectionEntry<C>>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
export function getEntry<
|
||||||
|
C extends keyof DataEntryMap,
|
||||||
|
E extends keyof DataEntryMap[C] | (string & {}),
|
||||||
|
>(
|
||||||
|
collection: C,
|
||||||
|
id: E,
|
||||||
|
): E extends keyof DataEntryMap[C]
|
||||||
|
? string extends keyof DataEntryMap[C]
|
||||||
|
? Promise<DataEntryMap[C][E]> | undefined
|
||||||
|
: Promise<DataEntryMap[C][E]>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
|
||||||
|
collection: C,
|
||||||
|
filter: string | LiveLoaderEntryFilterType<C>,
|
||||||
|
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
||||||
|
|
||||||
|
/** Resolve an array of entry references from the same collection */
|
||||||
|
export function getEntries<C extends keyof ContentEntryMap>(
|
||||||
|
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
|
||||||
|
): Promise<CollectionEntry<C>[]>;
|
||||||
|
export function getEntries<C extends keyof DataEntryMap>(
|
||||||
|
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
|
||||||
|
): Promise<CollectionEntry<C>[]>;
|
||||||
|
|
||||||
|
export function render<C extends keyof AnyEntryMap>(
|
||||||
|
entry: AnyEntryMap[C][string],
|
||||||
|
): Promise<RenderResult>;
|
||||||
|
|
||||||
|
export function reference<C extends keyof AnyEntryMap>(
|
||||||
|
collection: C,
|
||||||
|
): import('astro/zod').ZodEffects<
|
||||||
|
import('astro/zod').ZodString,
|
||||||
|
C extends keyof ContentEntryMap
|
||||||
|
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
|
||||||
|
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
|
||||||
|
>;
|
||||||
|
// Allow generic `string` to avoid excessive type errors in the config
|
||||||
|
// if `dev` is not running to update as you edit.
|
||||||
|
// Invalid collection names will be caught at build time.
|
||||||
|
export function reference<C extends string>(
|
||||||
|
collection: C,
|
||||||
|
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
|
||||||
|
|
||||||
|
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
||||||
|
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
|
||||||
|
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
||||||
|
>;
|
||||||
|
|
||||||
|
type ContentEntryMap = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
type DataEntryMap = {
|
||||||
|
"bio": Record<string, {
|
||||||
|
id: string;
|
||||||
|
render(): Render[".md"];
|
||||||
|
slug: string;
|
||||||
|
body: string;
|
||||||
|
collection: "bio";
|
||||||
|
data: InferEntrySchema<"bio">;
|
||||||
|
rendered?: RenderedContent;
|
||||||
|
filePath?: string;
|
||||||
|
}>;
|
||||||
|
"blog": Record<string, {
|
||||||
|
id: string;
|
||||||
|
render(): Render[".md"];
|
||||||
|
slug: string;
|
||||||
|
body: string;
|
||||||
|
collection: "blog";
|
||||||
|
data: InferEntrySchema<"blog">;
|
||||||
|
rendered?: RenderedContent;
|
||||||
|
filePath?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
type AnyEntryMap = ContentEntryMap & DataEntryMap;
|
||||||
|
|
||||||
|
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
||||||
|
infer TData,
|
||||||
|
infer TEntryFilter,
|
||||||
|
infer TCollectionFilter,
|
||||||
|
infer TError
|
||||||
|
>
|
||||||
|
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
||||||
|
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
||||||
|
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
|
||||||
|
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
||||||
|
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
||||||
|
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
||||||
|
|
||||||
|
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
|
||||||
|
LiveContentConfig['collections'][C]['schema'] extends undefined
|
||||||
|
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
|
||||||
|
: import('astro/zod').infer<
|
||||||
|
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
|
||||||
|
>;
|
||||||
|
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
|
||||||
|
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
|
||||||
|
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
|
||||||
|
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
|
||||||
|
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
|
||||||
|
LiveContentConfig['collections'][C]['loader']
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ContentConfig = typeof import("../src/content/config.js");
|
||||||
|
export type LiveContentConfig = never;
|
||||||
|
}
|
||||||
1
.astro/data-store.json
Normal file
5
.astro/settings.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"_variables": {
|
||||||
|
"lastUpdateCheck": 1767866746327
|
||||||
|
}
|
||||||
|
}
|
||||||
2
.astro/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/// <reference types="astro/client" />
|
||||||
|
/// <reference path="content.d.ts" />
|
||||||
4
.biomeignore
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
.astro
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
*.css
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Editor configuration, see https://editorconfig.org
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.ts]
|
|
||||||
quote_type = single
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
max_line_length = off
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
9
.env
|
|
@ -1 +1,8 @@
|
||||||
PERFORMANCE_PROFILER=true
|
# OpenWeatherMap API Key
|
||||||
|
# Get your free API key at: https://openweathermap.org/api
|
||||||
|
# Free tier includes 1,000 API calls per day
|
||||||
|
PUBLIC_OPENWEATHER_API_KEY=0938949580094b5d165c2e7cd23b7cf2
|
||||||
|
|
||||||
|
# Weather Location Configuration
|
||||||
|
PUBLIC_WEATHER_CITY=Rome
|
||||||
|
PUBLIC_WEATHER_COUNTRY_CODE=IT
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
PERFORMANCE_PROFILER=false
|
|
||||||
61
.github/workflows/main.yml
vendored
|
|
@ -1,32 +1,65 @@
|
||||||
name: Upload to S3
|
name: Deploy to S3
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
node-version: [20.x]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout code
|
||||||
- run: npm install
|
uses: actions/checkout@v4
|
||||||
- run: npm run build
|
|
||||||
- run: npm run generate-sitemap
|
- name: Setup Node.js
|
||||||
- uses: aws-actions/configure-aws-credentials@v1
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20.x'
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-cache
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Setup pnpm cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Type check
|
||||||
|
run: pnpm type-check
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Configure AWS credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@v4
|
||||||
with:
|
with:
|
||||||
aws-access-key-id: ${{ secrets.AWS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: eu-south-1
|
aws-region: eu-south-1
|
||||||
- run: aws s3 sync ./dist/loreiov.com/browser/ s3://${{ secrets.AWS_BUCKET }} --delete
|
|
||||||
- uses: chetan/invalidate-cloudfront-action@v2
|
- name: Sync to S3
|
||||||
|
run: aws s3 sync ./dist s3://${{ secrets.AWS_BUCKET }} --delete --cache-control "public, max-age=31536000, immutable"
|
||||||
|
|
||||||
|
- name: Invalidate CloudFront cache
|
||||||
|
uses: chetan/invalidate-cloudfront-action@v2
|
||||||
env:
|
env:
|
||||||
DISTRIBUTION: ${{ secrets.DISTRIBUTION }}
|
DISTRIBUTION: ${{ secrets.DISTRIBUTION }}
|
||||||
PATHS: "/index.html"
|
PATHS: "/*"
|
||||||
AWS_REGION: "eu-south-1"
|
AWS_REGION: "eu-south-1"
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
|
||||||
60
.gitignore
vendored
|
|
@ -1,46 +1,32 @@
|
||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
# build output
|
||||||
|
dist/
|
||||||
|
.output/
|
||||||
|
|
||||||
# Compiled output
|
# dependencies
|
||||||
/dist
|
node_modules/
|
||||||
/tmp
|
|
||||||
/out-tsc
|
|
||||||
/bazel-out
|
|
||||||
|
|
||||||
# Node
|
# logs
|
||||||
/node_modules
|
npm-debug.log*
|
||||||
npm-debug.log
|
yarn-debug.log*
|
||||||
yarn-error.log
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
# IDEs and editors
|
# environment variables
|
||||||
.idea/
|
.env
|
||||||
.project
|
.env.production
|
||||||
.classpath
|
|
||||||
.c9/
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# Visual Studio Code
|
# macOS-specific files
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.history/*
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
/.angular/cache
|
|
||||||
.sass-cache/
|
|
||||||
/connect.lock
|
|
||||||
/coverage
|
|
||||||
/libpeerconnection.log
|
|
||||||
testem.log
|
|
||||||
/typings
|
|
||||||
|
|
||||||
# System files
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
|
||||||
|
# jetbrains setting folders
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# vscode setting folders
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# amplify
|
# amplify
|
||||||
.amplify
|
.amplify
|
||||||
amplifyconfiguration*
|
amplifyconfiguration*
|
||||||
|
|
||||||
|
# misc
|
||||||
|
Thumbs.db
|
||||||
|
|
|
||||||
37
.prettierignore
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
.output/
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Package files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
.cache/
|
||||||
19
.prettierrc.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"plugins": ["prettier-plugin-astro"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.astro",
|
||||||
|
"options": {
|
||||||
|
"parser": "astro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
||||||
4
.vscode/extensions.json
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
|
||||||
"recommendations": ["angular.ng-template"]
|
|
||||||
}
|
|
||||||
20
.vscode/launch.json
vendored
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "ng serve",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "npm: start",
|
|
||||||
"url": "http://localhost:4200/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ng test",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"preLaunchTask": "npm: test",
|
|
||||||
"url": "http://localhost:9876/debug.html"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
42
.vscode/tasks.json
vendored
|
|
@ -1,42 +0,0 @@
|
||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "start",
|
|
||||||
"isBackground": true,
|
|
||||||
"problemMatcher": {
|
|
||||||
"owner": "typescript",
|
|
||||||
"pattern": "$tsc",
|
|
||||||
"background": {
|
|
||||||
"activeOnStart": true,
|
|
||||||
"beginsPattern": {
|
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "test",
|
|
||||||
"isBackground": true,
|
|
||||||
"problemMatcher": {
|
|
||||||
"owner": "typescript",
|
|
||||||
"pattern": "$tsc",
|
|
||||||
"background": {
|
|
||||||
"activeOnStart": true,
|
|
||||||
"beginsPattern": {
|
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
51
.zed/settings.json
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"languages": {
|
||||||
|
"Astro": {
|
||||||
|
"enable_language_server": true,
|
||||||
|
"formatter": "language_server",
|
||||||
|
"format_on_save": "on",
|
||||||
|
"tab_size": 2,
|
||||||
|
"hard_tabs": false
|
||||||
|
},
|
||||||
|
"TypeScript": {
|
||||||
|
"tab_size": 2,
|
||||||
|
"format_on_save": "on"
|
||||||
|
},
|
||||||
|
"TSX": {
|
||||||
|
"tab_size": 2,
|
||||||
|
"format_on_save": "on"
|
||||||
|
},
|
||||||
|
"CSS": {
|
||||||
|
"tab_size": 2,
|
||||||
|
"format_on_save": "on"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lsp": {
|
||||||
|
"astro": {
|
||||||
|
"settings": {
|
||||||
|
"astro": {
|
||||||
|
"format": {
|
||||||
|
"enable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"preferences": {
|
||||||
|
"importModuleSpecifier": "relative"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"language_server": {
|
||||||
|
"name": "prettier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"file_types": {
|
||||||
|
"Astro": ["astro"]
|
||||||
|
},
|
||||||
|
"tab_size": 2,
|
||||||
|
"format_on_save": "on",
|
||||||
|
"remove_trailing_whitespace_on_save": true,
|
||||||
|
"ensure_final_newline_on_save": true
|
||||||
|
}
|
||||||
31
README.md
|
|
@ -1,27 +1,20 @@
|
||||||
# LoreiovCom
|
# lorenzoiovino.com
|
||||||
|
|
||||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.8.
|
My personal website
|
||||||
|
|
||||||
## Development server
|
This project is built with [Astro](https://astro.build/) and [TailwindCSS](https://tailwindcss.com/).
|
||||||
|
|
||||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
## Tech Stack
|
||||||
|
|
||||||
## Code scaffolding
|
- **Framework**: [Astro](https://astro.build/) - Static site generator
|
||||||
|
- **Styling**: [TailwindCSS](https://tailwindcss.com/) - Utility-first CSS framework
|
||||||
|
- **Package Manager**: [pnpm](https://pnpm.io/)
|
||||||
|
- **Language**: TypeScript
|
||||||
|
|
||||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
## 🌐 Deployment
|
||||||
|
|
||||||
## Build
|
The site is configured to be deployed as a static site. The build output is in the `dist/` directory.
|
||||||
|
|
||||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
## Author
|
||||||
|
|
||||||
## Running unit tests
|
Lorenzo Iovino
|
||||||
|
|
||||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
||||||
|
|
||||||
## Running end-to-end tests
|
|
||||||
|
|
||||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
|
||||||
|
|
||||||
## Further help
|
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import { defineAuth } from '@aws-amplify/backend';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define and configure your auth resource
|
|
||||||
* When used alongside data, it is automatically configured as an auth provider for data
|
|
||||||
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
|
|
||||||
*/
|
|
||||||
export const auth = defineAuth({
|
|
||||||
loginWith: {
|
|
||||||
email: true,
|
|
||||||
// add social providers
|
|
||||||
externalProviders: {
|
|
||||||
/**
|
|
||||||
* first, create your secrets using `amplify sandbox secret`
|
|
||||||
* then, import `secret` from `@aws-amplify/backend`
|
|
||||||
* @see https://docs.amplify.aws/gen2/deploy-and-host/sandbox-environments/features/#setting-secrets
|
|
||||||
*/
|
|
||||||
// loginWithAmazon: {
|
|
||||||
// clientId: secret('LOGINWITHAMAZON_CLIENT_ID'),
|
|
||||||
// clientSecret: secret('LOGINWITHAMAZON_CLIENT_SECRET'),
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* enable multifactor authentication
|
|
||||||
* @see https://docs.amplify.aws/gen2/build-a-backend/auth/manage-mfa
|
|
||||||
*/
|
|
||||||
// multifactor: {
|
|
||||||
// mode: 'OPTIONAL',
|
|
||||||
// sms: {
|
|
||||||
// smsMessage: (code) => `Your verification code is ${code}`,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
userAttributes: {
|
|
||||||
/** request additional attributes for your app's users */
|
|
||||||
// profilePicture: {
|
|
||||||
// mutable: true,
|
|
||||||
// required: false,
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import { defineBackend } from '@aws-amplify/backend';
|
|
||||||
import { auth } from './auth/resource';
|
|
||||||
import { data } from './data/resource';
|
|
||||||
|
|
||||||
defineBackend({
|
|
||||||
auth,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
|
|
||||||
|
|
||||||
/*== STEP 1 ===============================================================
|
|
||||||
The section below creates a Todo database table with a "content" field. Try
|
|
||||||
adding a new "isDone" field as a boolean. The authorization rules below
|
|
||||||
specify that owners, authenticated via your Auth resource can "create",
|
|
||||||
"read", "update", and "delete" their own records. Public users,
|
|
||||||
authenticated via an API key, can only "read" records.
|
|
||||||
=========================================================================*/
|
|
||||||
const schema = a.schema({
|
|
||||||
Todo: a
|
|
||||||
.model({
|
|
||||||
content: a.string(),
|
|
||||||
})
|
|
||||||
.authorization([a.allow.owner(), a.allow.public().to(['read'])]),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Schema = ClientSchema<typeof schema>;
|
|
||||||
|
|
||||||
export const data = defineData({
|
|
||||||
schema,
|
|
||||||
authorizationModes: {
|
|
||||||
defaultAuthorizationMode: 'apiKey',
|
|
||||||
// API Key is used for a.allow.public() rules
|
|
||||||
apiKeyAuthorizationMode: {
|
|
||||||
expiresInDays: 30,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/*== STEP 2 ===============================================================
|
|
||||||
Go to your frontend source code. From your client-side code, generate a
|
|
||||||
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
|
|
||||||
WORK IN THE FRONTEND CODE FILE.)
|
|
||||||
|
|
||||||
Using JavaScript or Next.js React Server Components, Middleware, Server
|
|
||||||
Actions or Pages Router? Review how to generate Data clients for those use
|
|
||||||
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
|
|
||||||
=========================================================================*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
"use client"
|
|
||||||
import { generateClient } from "aws-amplify/data";
|
|
||||||
import { type Schema } from "@/amplify/data/resource";
|
|
||||||
|
|
||||||
const client = generateClient<Schema>() // use this Data client for CRUDL requests
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*== STEP 3 ===============================================================
|
|
||||||
Fetch records from the database and use them in your frontend component.
|
|
||||||
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
|
|
||||||
=========================================================================*/
|
|
||||||
|
|
||||||
/* For example, in a React component, you can use this snippet in your
|
|
||||||
function's RETURN statement */
|
|
||||||
// const { data: todos } = client.models.Todo.list()
|
|
||||||
|
|
||||||
// return <ul>{todos.map(todo => <li key={todo.id}>{todo.content}</li>)}</ul>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"type": "module"
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
|
||||||
|
|
||||||
/* Projects */
|
|
||||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
|
||||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
|
||||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
|
||||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
|
||||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
|
||||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
||||||
|
|
||||||
/* Language and Environment */
|
|
||||||
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
|
||||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
|
||||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
|
||||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
|
||||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
|
||||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
|
||||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
|
||||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
|
||||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
||||||
|
|
||||||
/* Modules */
|
|
||||||
"module": "es2022", /* Specify what module code is generated. */
|
|
||||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
|
||||||
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
||||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
|
||||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
|
||||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
|
||||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
|
||||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
|
||||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
|
||||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
|
||||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
|
||||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
|
||||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
|
||||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
|
||||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
|
||||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
|
||||||
|
|
||||||
/* JavaScript Support */
|
|
||||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
|
||||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
|
||||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
|
||||||
|
|
||||||
/* Emit */
|
|
||||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
|
||||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
|
||||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
|
||||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
|
||||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
|
||||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
|
||||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
|
||||||
// "removeComments": true, /* Disable emitting comments. */
|
|
||||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
|
||||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
|
||||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
|
||||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
|
||||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
|
||||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
|
||||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
|
||||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
|
||||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
|
||||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
|
||||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
|
||||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
|
||||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
|
||||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
|
||||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
|
||||||
|
|
||||||
/* Interop Constraints */
|
|
||||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
|
||||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
|
||||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
|
||||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
|
||||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
|
||||||
|
|
||||||
/* Type Checking */
|
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
|
||||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
|
||||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
|
||||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
|
||||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
|
||||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
|
||||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
|
||||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
|
||||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
|
||||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
|
||||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
|
||||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
|
||||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
|
||||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
|
||||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
|
||||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
|
||||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
|
||||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
|
||||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
|
||||||
|
|
||||||
/* Completeness */
|
|
||||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
||||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
106
angular.json
|
|
@ -1,106 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"version": 1,
|
|
||||||
"newProjectRoot": "projects",
|
|
||||||
"projects": {
|
|
||||||
"loreiov.com": {
|
|
||||||
"projectType": "application",
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "",
|
|
||||||
"sourceRoot": "src",
|
|
||||||
"prefix": "iov",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:application",
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist/loreiov.com",
|
|
||||||
"index": "src/index.html",
|
|
||||||
"browser": "src/main.ts",
|
|
||||||
"polyfills": [
|
|
||||||
"zone.js"
|
|
||||||
],
|
|
||||||
"tsConfig": "tsconfig.app.json",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": [
|
|
||||||
"src/favicon.ico",
|
|
||||||
"src/assets"
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": [],
|
|
||||||
"server": "src/main.server.ts",
|
|
||||||
"prerender": true,
|
|
||||||
"ssr": {
|
|
||||||
"entry": "server.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "500kb",
|
|
||||||
"maximumError": "1mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "2kb",
|
|
||||||
"maximumError": "4kb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputHashing": "all"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"optimization": false,
|
|
||||||
"extractLicenses": false,
|
|
||||||
"sourceMap": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"buildTarget": "loreiov.com:build:production"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"buildTarget": "loreiov.com:build:development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "development"
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"buildTarget": "loreiov.com:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"polyfills": [
|
|
||||||
"zone.js",
|
|
||||||
"zone.js/testing"
|
|
||||||
],
|
|
||||||
"tsConfig": "tsconfig.spec.json",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": [
|
|
||||||
"src/favicon.ico",
|
|
||||||
"src/assets"
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
astro.config.mjs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import tailwind from "@astrojs/tailwind";
|
||||||
|
import { defineConfig } from "astro/config";
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [tailwind(), sitemap()],
|
||||||
|
site: "https://lorenzoiovino.com",
|
||||||
|
compressHTML: true,
|
||||||
|
build: {
|
||||||
|
inlineStylesheets: "auto",
|
||||||
|
},
|
||||||
|
});
|
||||||
46
biome.json
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
|
||||||
|
"vcs": {
|
||||||
|
"enabled": true,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": true
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": true
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "tab",
|
||||||
|
"lineWidth": 100
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"complexity": {
|
||||||
|
"noExtraBooleanCast": "error",
|
||||||
|
"noUselessConstructor": "error",
|
||||||
|
"useFlatMap": "error"
|
||||||
|
},
|
||||||
|
"correctness": {
|
||||||
|
"noUnusedImports": "off",
|
||||||
|
"noUnusedVariables": "off"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"noNonNullAssertion": "warn",
|
||||||
|
"useConst": "error",
|
||||||
|
"useTemplate": "error"
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noExplicitAny": "error",
|
||||||
|
"noArrayIndexKey": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "double",
|
||||||
|
"semicolons": "always"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18977
package-lock.json
generated
69
package.json
|
|
@ -1,54 +1,31 @@
|
||||||
{
|
{
|
||||||
"name": "loreiov.com",
|
"name": "lorenzoiovino.com",
|
||||||
"version": "0.0.0",
|
"type": "module",
|
||||||
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"dev": "astro dev",
|
||||||
"start": "ng serve",
|
"start": "astro dev",
|
||||||
"build": "ng build",
|
"build": "astro check && astro build",
|
||||||
"generate-sitemap": "node ./node_modules/.bin/ngx-sitemap ./dist/loreiov.com/browser https://lorenzoiovino.com",
|
"preview": "astro preview",
|
||||||
"watch": "ng build --watch --configuration development",
|
"astro": "astro",
|
||||||
"test": "ng test",
|
"format": "prettier --write .",
|
||||||
"serve:ssr:loreiov.com": "node dist/loreiov.com/server/server.mjs"
|
"format:check": "prettier --check .",
|
||||||
|
"lint": "biome format src/",
|
||||||
|
"lint:fix": "biome format --write src/",
|
||||||
|
"type-check": "astro check"
|
||||||
},
|
},
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^17.0.0",
|
"@astrojs/tailwind": "^6.0.2",
|
||||||
"@angular/common": "^17.0.0",
|
"astro": "^5.16.7",
|
||||||
"@angular/compiler": "^17.0.0",
|
"tailwindcss": "^3.4.0"
|
||||||
"@angular/core": "^17.0.0",
|
|
||||||
"@angular/forms": "^17.0.0",
|
|
||||||
"@angular/platform-browser": "^17.0.0",
|
|
||||||
"@angular/platform-browser-dynamic": "^17.0.0",
|
|
||||||
"@angular/platform-server": "^17.0.0",
|
|
||||||
"@angular/router": "^17.0.0",
|
|
||||||
"@angular/ssr": "^17.0.8",
|
|
||||||
"aws-cdk-lib": "^2.117.0",
|
|
||||||
"constructs": "^10.3.0",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"ng2-fittext": "^1.4.0",
|
|
||||||
"rxjs": "~7.8.0",
|
|
||||||
"tslib": "^2.3.0",
|
|
||||||
"zone.js": "~0.14.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^17.0.8",
|
"@astrojs/check": "^0.9.6",
|
||||||
"@angular/cli": "^17.0.8",
|
"@astrojs/rss": "^4.0.14",
|
||||||
"@angular/compiler-cli": "^17.0.0",
|
"@astrojs/sitemap": "^3.6.1",
|
||||||
"@aws-amplify/backend": "^0.7.0",
|
"@biomejs/biome": "^2.3.11",
|
||||||
"@aws-amplify/backend-cli": "^0.9.2",
|
"prettier": "^3.7.4",
|
||||||
"@types/express": "^4.17.17",
|
"prettier-plugin-astro": "^0.14.1",
|
||||||
"@types/jasmine": "~5.1.0",
|
"typescript": "^5.3.0"
|
||||||
"@types/node": "^18.18.0",
|
|
||||||
"autoprefixer": "^10.4.16",
|
|
||||||
"jasmine-core": "~5.1.0",
|
|
||||||
"karma": "~6.4.0",
|
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
|
||||||
"karma-coverage": "~2.2.0",
|
|
||||||
"karma-jasmine": "~5.1.0",
|
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
|
||||||
"ngx-sitemap": "^1.0.0",
|
|
||||||
"postcss": "^8.4.32",
|
|
||||||
"tailwindcss": "^3.4.0",
|
|
||||||
"typescript": "5.2.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4519
pnpm-lock.yaml
generated
Normal file
3
pnpm-workspace.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
ignoredBuiltDependencies:
|
||||||
|
- esbuild
|
||||||
|
- sharp
|
||||||
2
public/astro.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title>file_type_astro</title><path d="M5.9,18.847a7.507,7.507,0,0,0-.572,2.624,3.265,3.265,0,0,0,.551,1.553,7.427,7.427,0,0,0,2.093,1.681L13.1,28.119A7.332,7.332,0,0,0,15.2,29.287a3.239,3.239,0,0,0,1.5,0,7.381,7.381,0,0,0,2.117-1.16L24,24.711a7.512,7.512,0,0,0,2.117-1.688,3.241,3.241,0,0,0,.55-1.563,7.515,7.515,0,0,0-.587-2.643L21.547,4.551a3.973,3.973,0,0,0-.54-1.3,1.733,1.733,0,0,0-.7-.51,3.972,3.972,0,0,0-1.4-.122H13.005a3.932,3.932,0,0,0-1.4.125,1.713,1.713,0,0,0-.7.512,3.94,3.94,0,0,0-.535,1.3L5.9,18.848Zm13.24-13.2a3.329,3.329,0,0,1,.441,1.093l3.892,12.784a16.168,16.168,0,0,0-4.653-1.573L16.291,9.391a.331.331,0,0,0-.513-.169.323.323,0,0,0-.119.169l-2.5,8.557a16.14,16.14,0,0,0-4.674,1.579L12.393,6.743a3.281,3.281,0,0,1,.442-1.094,1.458,1.458,0,0,1,.582-.43,3.31,3.31,0,0,1,1.175-.1h2.793a3.314,3.314,0,0,1,1.176.1,1.454,1.454,0,0,1,.583.432ZM16.127,21.06a5.551,5.551,0,0,0,3.4-.923,2.8,2.8,0,0,1-.207,2.182A3.938,3.938,0,0,1,17.773,23.8c-.674.428-1.254.8-1.254,1.787a2.079,2.079,0,0,0,.209.914,2.49,2.49,0,0,1-1.535-2.3v-.061c0-.683,0-1.524-.962-1.524a1.028,1.028,0,0,0-.391.077,1.021,1.021,0,0,0-.552.551,1.03,1.03,0,0,0-.079.391,3.769,3.769,0,0,1-.988-2.644,4.206,4.206,0,0,1,.175-1.248c.4.757,1.92,1.32,3.731,1.32Z" style="fill:#ff5d01;fill-rule:evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 705 KiB After Width: | Height: | Size: 705 KiB |
|
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 570 KiB After Width: | Height: | Size: 570 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
59
public/manifest.json
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
"name": "Lorenzo Iovino - Software Developer",
|
||||||
|
"short_name": "Lorenzo Iovino",
|
||||||
|
"description": "Software Developer based in Sicily. Passionate about technology, remote work, and life balance.",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#1e3a8a",
|
||||||
|
"theme_color": "#1e3a8a",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/favicon.ico",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/photos/me.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/photos/me.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"categories": ["business", "productivity", "lifestyle"],
|
||||||
|
"lang": "en-US",
|
||||||
|
"dir": "ltr",
|
||||||
|
"scope": "/",
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "Blog",
|
||||||
|
"short_name": "Blog",
|
||||||
|
"description": "Read my latest blog posts",
|
||||||
|
"url": "/blog",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/photos/me.png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bio",
|
||||||
|
"short_name": "Bio",
|
||||||
|
"description": "Learn about my story",
|
||||||
|
"url": "/bio",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/photos/me.png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 414 KiB |
|
Before Width: | Height: | Size: 3 MiB After Width: | Height: | Size: 3 MiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 224 KiB After Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 939 KiB After Width: | Height: | Size: 939 KiB |
|
Before Width: | Height: | Size: 519 KiB After Width: | Height: | Size: 519 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 11 MiB |
|
Before Width: | Height: | Size: 421 KiB After Width: | Height: | Size: 421 KiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 706 KiB After Width: | Height: | Size: 706 KiB |
32
public/robots.txt
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# robots.txt for lorenzoiovino.com
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Sitemaps
|
||||||
|
Sitemap: https://lorenzoiovino.com/sitemap-index.xml
|
||||||
|
Sitemap: https://lorenzoiovino.com/rss.xml
|
||||||
|
|
||||||
|
# Crawl-delay (optional, adjust if needed)
|
||||||
|
# Crawl-delay: 1
|
||||||
|
|
||||||
|
# Disallow paths (if any)
|
||||||
|
# Disallow: /admin/
|
||||||
|
# Disallow: /private/
|
||||||
|
|
||||||
|
# Allow specific bots
|
||||||
|
User-agent: Googlebot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Bingbot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Slurp
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Block bad bots (optional)
|
||||||
|
User-agent: AhrefsBot
|
||||||
|
Crawl-delay: 10
|
||||||
|
|
||||||
|
User-agent: SemrushBot
|
||||||
|
Crawl-delay: 10
|
||||||
234
public/rss-styles.xsl
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||||
|
<xsl:template match="/">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title><xsl:value-of select="/rss/channel/title"/> - RSS Feed</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1f2937;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.header p {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.info-box {
|
||||||
|
background: #f3f4f6;
|
||||||
|
border-left: 4px solid #3b82f6;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
.info-box h2 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: #1e3a8a;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.info-box p {
|
||||||
|
color: #4b5563;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
.info-box code {
|
||||||
|
background: white;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
color: #1e3a8a;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.subscribe-btn {
|
||||||
|
display: inline-block;
|
||||||
|
background: #3b82f6;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 1rem;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
.subscribe-btn:hover {
|
||||||
|
background: #2563eb;
|
||||||
|
}
|
||||||
|
.posts {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.posts h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: #111827;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
border-bottom: 2px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
.post {
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
.post:hover {
|
||||||
|
background: #f9fafb;
|
||||||
|
}
|
||||||
|
.post:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.post-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.post-title a {
|
||||||
|
color: #1e3a8a;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.post-title a:hover {
|
||||||
|
color: #3b82f6;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.post-meta {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.post-description {
|
||||||
|
color: #4b5563;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
.tag {
|
||||||
|
background: #dbeafe;
|
||||||
|
color: #1e3a8a;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.footer a {
|
||||||
|
color: #3b82f6;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
body {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
.info-box, .posts {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>📡 <xsl:value-of select="/rss/channel/title"/></h1>
|
||||||
|
<p><xsl:value-of select="/rss/channel/description"/></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box">
|
||||||
|
<h2>🎯 What is an RSS Feed?</h2>
|
||||||
|
<p>
|
||||||
|
This is an RSS feed. It allows you to subscribe to updates from this blog using an RSS reader.
|
||||||
|
Copy the URL <code><xsl:value-of select="/rss/channel/link"/>/rss.xml</code> and paste it into your favorite RSS reader to stay updated!
|
||||||
|
</p>
|
||||||
|
<a href="https://aboutfeeds.com/" target="_blank" class="subscribe-btn">Learn More About RSS</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="posts">
|
||||||
|
<h2>Recent Posts (<xsl:value-of select="count(/rss/channel/item)"/>)</h2>
|
||||||
|
<xsl:for-each select="/rss/channel/item">
|
||||||
|
<div class="post">
|
||||||
|
<div class="post-title">
|
||||||
|
<a>
|
||||||
|
<xsl:attribute name="href">
|
||||||
|
<xsl:value-of select="link"/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:value-of select="title"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="post-meta">
|
||||||
|
<span>📅 <xsl:value-of select="substring(pubDate, 0, 17)"/></span>
|
||||||
|
<xsl:if test="author">
|
||||||
|
<span>•</span>
|
||||||
|
<span>✍️ <xsl:value-of select="author"/></span>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:if test="category">
|
||||||
|
<span>•</span>
|
||||||
|
<xsl:for-each select="category">
|
||||||
|
<span class="tag"><xsl:value-of select="."/></span>
|
||||||
|
</xsl:for-each>
|
||||||
|
</xsl:if>
|
||||||
|
</div>
|
||||||
|
<div class="post-description">
|
||||||
|
<xsl:value-of select="description"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</xsl:for-each>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>
|
||||||
|
Powered by <a href="https://astro.build" target="_blank">Astro</a> •
|
||||||
|
Visit <a>
|
||||||
|
<xsl:attribute name="href">
|
||||||
|
<xsl:value-of select="/rss/channel/link"/>
|
||||||
|
</xsl:attribute>
|
||||||
|
lorenzoiovino.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
||||||
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 772 B |
58
server.ts
|
|
@ -1,58 +0,0 @@
|
||||||
import { APP_BASE_HREF } from '@angular/common';
|
|
||||||
import { CommonEngine } from '@angular/ssr';
|
|
||||||
import express from 'express';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
import { dirname, join, resolve } from 'node:path';
|
|
||||||
import bootstrap from './src/main.server';
|
|
||||||
|
|
||||||
// The Express app is exported so that it can be used by serverless Functions.
|
|
||||||
export function app(): express.Express {
|
|
||||||
const server = express();
|
|
||||||
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
|
|
||||||
const browserDistFolder = resolve(serverDistFolder, '../browser');
|
|
||||||
const indexHtml = join(serverDistFolder, 'index.server.html');
|
|
||||||
|
|
||||||
const commonEngine = new CommonEngine({
|
|
||||||
enablePerformanceProfiler: process.env['PERFORMANCE_PROFILER'] === 'true',
|
|
||||||
});
|
|
||||||
|
|
||||||
server.set('view engine', 'html');
|
|
||||||
server.set('views', browserDistFolder);
|
|
||||||
|
|
||||||
// Example Express Rest API endpoints
|
|
||||||
// server.get('/api/**', (req, res) => { });
|
|
||||||
// Serve static files from /browser
|
|
||||||
server.get('*.*', express.static(browserDistFolder, {
|
|
||||||
maxAge: '1y'
|
|
||||||
}));
|
|
||||||
|
|
||||||
// All regular routes use the Angular engine
|
|
||||||
server.get('*', (req, res, next) => {
|
|
||||||
const { protocol, originalUrl, baseUrl, headers } = req;
|
|
||||||
|
|
||||||
commonEngine
|
|
||||||
.render({
|
|
||||||
bootstrap,
|
|
||||||
documentFilePath: indexHtml,
|
|
||||||
url: `${protocol}://${headers.host}${originalUrl}`,
|
|
||||||
publicPath: browserDistFolder,
|
|
||||||
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
|
|
||||||
})
|
|
||||||
.then((html) => res.send(html))
|
|
||||||
.catch((err) => next(err));
|
|
||||||
});
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(): void {
|
|
||||||
const port = process.env['PORT'] || 4000;
|
|
||||||
|
|
||||||
// Start up the Node server
|
|
||||||
const server = app();
|
|
||||||
server.listen(port, () => {
|
|
||||||
console.log(`Node Express server listening on http://localhost:${port}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<div class="flex flex-col h-screen justify-between">
|
|
||||||
<header class="inset-x-0 top-0 z-50 bg-secondary">
|
|
||||||
<iov-menu></iov-menu>
|
|
||||||
</header>
|
|
||||||
<router-outlet>
|
|
||||||
</router-outlet>
|
|
||||||
<iov-footer></iov-footer>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [AppComponent],
|
|
||||||
}).compileComponents();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the app', () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.componentInstance;
|
|
||||||
expect(app).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should have the 'loreiov.com' title`, () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.componentInstance;
|
|
||||||
expect(app.title).toEqual('loreiov.com');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render title', () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
const compiled = fixture.nativeElement as HTMLElement;
|
|
||||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, loreiov.com');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import {RouterOutlet} from '@angular/router';
|
|
||||||
import {MenuComponent} from "./menu/menu.component";
|
|
||||||
import {HeroComponent} from "./hero/hero.component";
|
|
||||||
import {SectionComponent} from "./section/section.component";
|
|
||||||
import {FooterComponent} from "./footer/footer.component";
|
|
||||||
import {Meta} from "@angular/platform-browser";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-root',
|
|
||||||
standalone: true,
|
|
||||||
imports: [CommonModule, RouterOutlet, MenuComponent, HeroComponent, SectionComponent, FooterComponent],
|
|
||||||
templateUrl: './app.component.html',
|
|
||||||
styleUrl: './app.component.scss'
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
|
|
||||||
constructor(private metaTagService: Meta) {
|
|
||||||
this.metaTagService.addTags([
|
|
||||||
{ name: 'description', content: 'Lorenzo Iovino - Software Developer' },
|
|
||||||
{
|
|
||||||
name: 'keywords',
|
|
||||||
content: 'Lorenzo Iovino, lorenzoiovino.com, Software Developer, Software Engineer, Sicily, Computer Science, Blog, Personal Page',
|
|
||||||
},
|
|
||||||
{ name: 'robots', content: 'index, follow' },
|
|
||||||
{ name: 'author', content: 'Lorenzo Iovino' },
|
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
||||||
{ charset: 'UTF-8' },
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
|
|
||||||
import { provideServerRendering } from '@angular/platform-server';
|
|
||||||
import { appConfig } from './app.config';
|
|
||||||
|
|
||||||
const serverConfig: ApplicationConfig = {
|
|
||||||
providers: [
|
|
||||||
provideServerRendering()
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { ApplicationConfig } from '@angular/core';
|
|
||||||
import { provideRouter } from '@angular/router';
|
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
|
||||||
import { provideClientHydration } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
|
||||||
providers: [provideRouter(routes), provideClientHydration()]
|
|
||||||
};
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
import { Routes } from '@angular/router';
|
|
||||||
import {BiographyPage} from "./pages/biography/biography.page";
|
|
||||||
import {BlogPage} from "./pages/blog/blog.page";
|
|
||||||
import {ProjectsPage} from "./pages/projects/projects.page";
|
|
||||||
import {HomePage} from "./pages/home/home.page";
|
|
||||||
import {PortfolioPage} from "./pages/portfolio/portfolio.page";
|
|
||||||
import {ContactMePage} from "./pages/contact-me/contact-me.page";
|
|
||||||
import {PageComponent} from "./page/page.component";
|
|
||||||
import {LikeDislikePage} from "./pages/like-dislike/like-dislike.page";
|
|
||||||
import {DisclaimerComponent as PortfolioDisclaimerComponent} from "./pages/portfolio/disclaimer/disclaimer.component";
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: HomePage
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: PageComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'biography',
|
|
||||||
component: BiographyPage,
|
|
||||||
},
|
|
||||||
/*{
|
|
||||||
path : 'portfolio',
|
|
||||||
component: PageComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: PortfolioPage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'disclaimer',
|
|
||||||
component: PortfolioDisclaimerComponent,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'projects',
|
|
||||||
component: ProjectsPage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'like-dislike',
|
|
||||||
component: LikeDislikePage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path : 'blog',
|
|
||||||
component: BlogPage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'hello',
|
|
||||||
component: ContactMePage,
|
|
||||||
}*/
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<div class="arrow left-1/2" (click)="scrollDown()" *ngIf="visible">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
.arrow {
|
|
||||||
position: absolute;
|
|
||||||
transform: translate(-50%, -50%) rotate(0deg);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow span {
|
|
||||||
display: block;
|
|
||||||
width: 1.5vw;
|
|
||||||
height: 1.5vw;
|
|
||||||
border-bottom: 5px solid white;
|
|
||||||
border-right: 5px solid white;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
margin: -10px;
|
|
||||||
animation: animate 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow span:nth-child(2) {
|
|
||||||
animation-delay: -0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow span:nth-child(3) {
|
|
||||||
animation-delay: -0.4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animate {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: rotate(45deg) translate(-20px, -20px);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: rotate(45deg) translate(20px, 20px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ArrowScrollDownComponent } from './arrow-scroll-down.component';
|
|
||||||
|
|
||||||
describe('ArrowScrollDownComponent', () => {
|
|
||||||
let component: ArrowScrollDownComponent;
|
|
||||||
let fixture: ComponentFixture<ArrowScrollDownComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [ArrowScrollDownComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ArrowScrollDownComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import {Component, HostListener} from '@angular/core';
|
|
||||||
import {NgIf} from "@angular/common";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-arrow-scroll-down',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
NgIf
|
|
||||||
],
|
|
||||||
templateUrl: './arrow-scroll-down.component.html',
|
|
||||||
styleUrl: './arrow-scroll-down.component.scss'
|
|
||||||
})
|
|
||||||
export class ArrowScrollDownComponent {
|
|
||||||
|
|
||||||
visible: boolean = true;
|
|
||||||
|
|
||||||
scrollDown() {
|
|
||||||
window.scrollBy({
|
|
||||||
top: window.innerHeight,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener('window:scroll', ['$event'])
|
|
||||||
checkIfPageIsStillOnTop() {
|
|
||||||
this.visible = window.scrollY <= 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<pre>
|
|
||||||
{{photos[index]}}
|
|
||||||
</pre>
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { AsciiPhotoComponent } from './ascii-photo.component';
|
|
||||||
|
|
||||||
describe('AsciiPhotoComponent', () => {
|
|
||||||
let component: AsciiPhotoComponent;
|
|
||||||
let fixture: ComponentFixture<AsciiPhotoComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [AsciiPhotoComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(AsciiPhotoComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
import {Component, Input} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-ascii-photo',
|
|
||||||
standalone: true,
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './ascii-photo.component.html',
|
|
||||||
styleUrl: './ascii-photo.component.scss'
|
|
||||||
})
|
|
||||||
export class AsciiPhotoComponent {
|
|
||||||
@Input() index: number = 0;
|
|
||||||
photos = [`
|
|
||||||
**#*+====*#%%@@%%@%%+==+#######*++*##*+*++*#%*=========================+++*+*++++++=+===============
|
|
||||||
*##*======+###%%@%%%+==+*##%%%##++###*+*=+##*=====================+++**#*******#*+++++==============
|
|
||||||
##*+=======+***%%@%%#==++#%%%%##**###*++++#*+==++==============++***##*+=====++=++**++==============
|
|
||||||
*#++=========****#%%##%**#%%%%##*####++=+*##==++++====++=====++*####*===============+===============
|
|
||||||
**++======--==+*#%%#%%%*+#%%%%%%###**+=+*######**++***====+**#####+=================================
|
|
||||||
**++*+++=+=====+*##%%%%#+*#%%%%%###***+**###*=++*##*+=-==**####++++=====-===========================
|
|
||||||
******####*+++==+**##%%#*+*#%%%%##***+*#%##++**##*=====+**##+=++++===--===-----------------=========
|
|
||||||
**+**#++==+*#%#**+#+#%%%##*#%%%%%#*#*+#%##*##*#+===-=*+*#*===+***++++==-----------------------------
|
|
||||||
*+++===-====-=+####*###%##*##%%%%#*##*####*+#*=-===+*##+-==+**###*+=++================--------------
|
|
||||||
+*##+=-==========#####*#######%%%#*#*####+##+---==+**=-==++####++=+##+*+**####***+=-----------------
|
|
||||||
#++=+##+******+**++#%#########%%%%##*###+*#=----++*+--==+**%##*###############****+====-------------
|
|
||||||
#%#+*++##*++------+**#%#+#####%%%##**##***----=+*+--==***##*####*+===------=+++*####*++==-----------
|
|
||||||
*####%#+==*+=-------+###**#####%%##**#*+*=---==*=--++*####*+++==-------------====*###*=-------------
|
|
||||||
===**#%#*++=+==-------*#######%%%#######+--==++--=+=*#+=--------------------==--=+++**=-=-----------
|
|
||||||
---=----+*#*+=+=---====+##*##%%%%#%####**+**##*+=+**=-=----------:::::::::-=++--====+*++---:::::::--
|
|
||||||
------------=**+*=======+#**#%###%%######*+*+=-**+---:-----::::::::::::::::-==----====+==--:::::::::
|
|
||||||
-=======-=++=++*#**++=-==+#*#%#######+###**+==++=*=====---:::::::::::::::::-++==--=-===*++====+++===
|
|
||||||
=+**********+--=+***++--===++#####%**##*#*=+++###%%%%%@#=-================+=======++==++*++=--------
|
|
||||||
*+-::::-==+++++=---++*+----+=*#*###**#**++*#%%%%%%%%%@@@%******+++==-----------------==++====-------
|
|
||||||
++===+=+===+====+++==*+++===**#*##*####+*#%%%%%%%%%%%%**#%%%%%%%%%%%%#%%#-----------=-=+====------==
|
|
||||||
+*****+===-=---------==****+*##*###+*###%%%%%%%%#%###+++*%%%@@%%%%%%@@@@@#+++++++++**+**+*+++++=++++
|
|
||||||
-:-----:------==--::::-+*##*=#%##%%#%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%@@@%%%##*++**+=++*+++++==+++===
|
|
||||||
:--========+======+*++**######%%%#%%%%%%%%%%%%%%@@@@%#####%%%%%%%@@@%%%%%@@%###*+====-====--=----==-
|
|
||||||
+*++++***+++++++**+**####%%%%%%#%@%%%%%%%%%%#**+++++++++++++++*#%@@@@@@@%@%%%**##**==---------------
|
|
||||||
=+===+=--=+=-===-=**###%%%%%%%##%%%%%%%%%#++=========+++==++++++++**#%@@@@%%#+--#*++=---------------
|
|
||||||
-------------=+*######**%%%%%%%#%%%%%%#++===================+++++++++**%@%%%%#+-:+##++=---:::--::-::
|
|
||||||
:::::------:-*%##=###+***+%#@@%%%%%%*++===========================+++++**%@@@%%*---##+=---:-----====
|
|
||||||
::::--::::-=**#*+#%**++*++%@%%%%%@%*++============================+++++++*#%@@%#=-:-*##*++++++++++++
|
|
||||||
-:----:::=*#*#**##**+++*+=#@%%%%@#+++===============================+++++++*%@@%*:::-+***=----::----
|
|
||||||
:::---:-=##**#-*#*=--:=*:::-#%@%*++++==============================+++++++++*%%@%=:::-+*+=----------
|
|
||||||
--------+###*++##*=---=*---+%%#++++++================================++++++++*%@%*::::=***#*########
|
|
||||||
::::-:-+##*++=**#=+########%%#+++++++================================++++++++*%%@%-::::=******#*****
|
|
||||||
-:::--+#*#*=-=#+*-+******##%%*+++++===============================+++++++++***#%@%-:::::=*****##****
|
|
||||||
:-::-+###*=:-=*+--+******#%%#++++++++****#######*++++===========+++++++++++****%%%-::::-:*****##****
|
|
||||||
::---****=-::--=:-*******#%%*++++++*#%%%%%%%%%%%###**+++++++++++******+++++++**#%#-::::::+*****#****
|
|
||||||
-::-=*+++-::::--:-******##@%*++++*#####*******######**+++++++**###%%%%%###**+**#%+::::::--*****#**++
|
|
||||||
:::-=*+=-::::::::-******#%@%*+++*####*********######**+===++*###%%%%%%%%%%%%#**#%=::::::--+****#****
|
|
||||||
-::-+*=-:::::::::=******#%@%*+++**####%%%%%%%%%%%%#**++====+*##%%#########%%%#*%#:::::::::=****##***
|
|
||||||
::--++--:::::::::+******#*@#++++***##%%%#%@@@@##%%#**+======+#%%%%%########%%%#%-:::::::-:-*****#***
|
|
||||||
---------:::::---**++***#*##+++++***********####****++======+*%%#%@@@@@%%%#####*::::::::::-*****#***
|
|
||||||
---:::-:::::::---**++****++*+++++++++************+++=========**#####%##%%%%##*#=::::::::---*******++
|
|
||||||
-----------:::---#*+++++#*+*++++======+++*****+++============+****#*########***=========++++++++++++
|
|
||||||
=============++==+====+*****+++=========================--===+*****#***********+----:-----::----::::
|
|
||||||
==============--=-----+*++*#*++++================+====----====++++++***+++++++**=:::-::::--::::::-::
|
|
||||||
------=----------::--:=++*##**+++============+++=====------====+++++++++++++++**---------========--=
|
|
||||||
------=--------=------=+**#%#**+++=++=====++++++======-----===++**++++++=+++***+=++++++*++++++++++++
|
|
||||||
*+++++::::::--:::...::-+++*%#**+++++++++++**+=+++++++++=====+++*+**+++++++++**#+++++*+++++++++++===+
|
|
||||||
*##**+::::::::::::-=+==+++*%%#*++*#*+++***+====+*%%%%#**++++*****+++++++*++**##=----------------::::
|
|
||||||
==+++-::::::::::=*#**#**+++#%#*++**++***++++++++*##########%@%#**++++++******%#-:::::::::-::--:-----
|
|
||||||
--=--:::::::::::+**###*##+*%%#*****+*#****#####%#%%%%%%%%%%@@%%#**+++**+****##+:::::::::-:::-::::-::
|
|
||||||
==--=::::::::::-+******#@@@%%#*+***+*####%%%%%%%%%%%%%%%%%@%@%%%%####*******##=--:::::-:-::::::::--:
|
|
||||||
---=-::::::::::=+******#@@@@%#***##*###%%%%%#######**##%%##%%%%%%%%%%##*****##+-:-::::--::--------=-
|
|
||||||
-----:::::::::-=======-*@@@@%%#*###**##%@%%##%#################%%%%%%%%#######=-------------------:-
|
|
||||||
-----::--:==--=+=--::::*@@@@%%###%%#*##%#**++++++++++++++++**###%%@@@%%######+=:------------:--:-:--
|
|
||||||
----:::---:-::===--::::=%@@@@%%##%%%###**+++++++**++++++++++++*****#%%##%%%##=-:::::--------:-::----
|
|
||||||
--=-:::::::::::-========*@@@@@%%%%%%%###**++==++*##############*****#%##%%%%+-:::::::--------------=
|
|
||||||
----::::::::::::::::::::::*%@@@%%%%%%%%%##*++++++***##%%%%###**++***#%%%%%%*-::::::::-:::-----=--=*#
|
|
||||||
----:::::::::::-:::::-:::-=*#*#@@%%%%%%%%##*+++++++**#######**++****#%%%%%%=::::::::---:::::---=*###
|
|
||||||
--=-==++++*#########%%%%%%###***%%%%%%%%%##*+++++++++********++***#%%%%@@%+-----::-------------***##
|
|
||||||
----==++=+#%%%@%%%%%%%%%%%#****+*%%%%%%%%%%#****++++++++++*+*++**#%%%%%@#=---------------------*####
|
|
||||||
----=-====################****+++**%@@%%%%%%##**#****++****#***###%%%%@%+----:-----------------+#*%#
|
|
||||||
=-=======+#%%%%%%%%%%%%%%%#****++***%@@@@%%%%%######******#####%%%%%@@#++---::::::-:::-:-------=+***
|
|
||||||
-========+#%%@@@@%%%%%%%%%#****++++**#%%%%%@%%%%%############%%%%@@@@%###*************+::------=*++*
|
|
||||||
---=-==--+#%%%@@%%@%*##%%#****++++++**##%@@@@@@%%%%%%%%%%%%%%%%@@@@%#*%%%%%%%%%%%%%%%%#--------=*#*#
|
|
||||||
+++++*+++*#########****##****+++++++++**#%@@@@@@@%%%%%%%%%%%%@@@@%#***%%%%%%%%%%%%%%%%#========-++*+
|
|
||||||
--=-=====*##%%%@%%@#####+**++++++++++++***##%@@@@@@@@%@@@@@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%##+++*##*
|
|
||||||
==+=====+#%#%%%%%%#+==--=*+++++++++++++++*****#%%@@@@@@@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=+##*
|
|
||||||
========+#%#%%#+===-==---++++++++++++++++++******##########**#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+=***
|
|
||||||
--======*#*=====+=--==----+++++++++++++++++++****************#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+=***
|
|
||||||
=-=============++===-==---=**++++++++++++++++++**************#%%%%%%%%%#%%%%##%%#%#%%#%%%%%%%%%+=+++
|
|
||||||
=======---===+++====--=----=**+++++++++++++++++++***********+#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*=+*+
|
|
||||||
========--=++++======-===----+*+++++++++++++++++++++++****+++#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+=+##
|
|
||||||
=====+==--=+++=========-==-----++++++++++++++++++++++++++*+++#%%%%%%%%%%%%#%%%%%%#%#%%%%%%%%%%%+====
|
|
||||||
======+==--=++===-======-==-----=++++++++++++++++++++++*+++++#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%+====
|
|
||||||
=======+==-=++===-=======--==------=**+++++++************+++*#=-------------------------------*+=+==
|
|
||||||
===========-+++===-=======---=--------=++++**************++*+#-::::::=-=--=--==---==-:=-::::::*+=++=
|
|
||||||
=====+======+++======-=====----=---------==++******#***++====*-:::::-:---:-::---:-:--:=-::::::*+=++=
|
|
||||||
====++==---=+++===-==-=======----==-----=====================*-:::::::::::::::::::::::::::::::*+==++
|
|
||||||
====++===--==+++=====---========----==-----==================*-:::::::::::::::::::::::::::::::*+==++
|
|
||||||
===+++====-==+++======--============--====----===============*-:::::::::::::::::::::::::::::::*+==++
|
|
||||||
+++++++==--==+*+=======--====================================****=-:::::::::::::::::::::::-+*+#+==++
|
|
||||||
+++++++==---=+*++=======-=====================================##+-:=+#*=-:::::::::::-+*#+-:-+#*===++
|
|
||||||
+++++++==--==+*++========--=======================================+***+====+*+++++=-=+***++=++====++
|
|
||||||
+++++++==---=+*++========================================================+*##+=*##*+++======+++====+
|
|
||||||
++++++====--=+*++===================================================================++=======++====+
|
|
||||||
+++++++===--==*++===================================================================+++=======+++==+
|
|
||||||
+++++++===--==*++===================================================================+++========+++=+
|
|
||||||
==+++++==---==*++===================================================================+++=========++++
|
|
||||||
==+++++===--==**++===========================================++=====================+*+==========+++
|
|
||||||
===+++++===-==**++=+==+===================+=++===============+==================+===+*++===+====++++
|
|
||||||
+===++++======**++++=+++=========+==============+++=========++=================++===+*++========++++
|
|
||||||
+===+++++=====+*++++++++==================================++++=================+++==+**++=+====+++++
|
|
||||||
+====++++=====+*+++++++===========++==+=======+===++=+===+++++================++++===+*++=++===+++++
|
|
||||||
+====++++=====+*+++++++==========+++++++==+=++=+++++====+++++===============+=++++===+*++==+=++++*++
|
|
||||||
++++=++++=====+*++++++============+++++==++++++++++++++++++++================++++++==+*+++==+++++*++
|
|
||||||
++++==+++=====+*++++++++==========+++++++=+++++++++++++++++++======+========+++++++==+*+++=++++++*++
|
|
||||||
*++++=+++=====+*+++++++==========++++++++++++++++++++++++++++++++===========+++++++==+*+++++++++*+++`,
|
|
||||||
`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
´›¦)vòri¯…\`
|
|
||||||
\`ceåëëýýÖÒÞŸj°‚
|
|
||||||
\`¯ôÚðÒkµ5ç±Cò‰öVµ0)´
|
|
||||||
…?§åÝú±sƒorvvjvjctu2sº
|
|
||||||
…>DU©ó‰JIrì[[[7>ï1c¤JÌ0¡\`
|
|
||||||
tÓxLÌò¼¤jl<[¿[[¿7ì1o%‡Ìw”\`
|
|
||||||
¨žå©çVósƒrï†?¿[¿†ïcJó±ò‰©Í:\`
|
|
||||||
‚Þxúô20C‡IîíiiïcCΞ5ùaaÍsÎï·
|
|
||||||
…äf5õaÍönôx5©¢í=ÏhZÿÎ2Í@½‰y½ò¨
|
|
||||||
Jšyf&6àkÎUDÒµ‰=ï%@03½trî%Ï5C;
|
|
||||||
~ÇTOSZád$àwVçÍI[†ïvjrîoc%Ïkçcˆ
|
|
||||||
…iZy9ààžyôóuLLJ?/[1IJsu@CóUyj‹
|
|
||||||
\`›£Çx5Vs¤î1oó2eS5§Úx5aaLCÌüÝr…
|
|
||||||
\`’shSξç@u‡z0šGŽÐéñSUhbá¾f2ô•
|
|
||||||
\`~TñèDŸ6T5ÏÍyñÓýšL‰ƒ½tcuõž6x¡
|
|
||||||
·+9ÓmëkUSfÇâpDµõykUCcrsfÝžS¹
|
|
||||||
·*üámÓTkFášOúúaYV%ïju™O§žì\`
|
|
||||||
\`ˆ›¯Ddñ$m8èP6™YC¤oCxhb8ôv´
|
|
||||||
¨?ÕqÕAAqA$ÙÿZäåðŠA¾sï;
|
|
||||||
|ZgXgHÁ#Nþæ##ê®Í½1i¡\`
|
|
||||||
›Vx§áåðH#ê$Ÿõ£sIí>׫˜¨\`
|
|
||||||
!bõYaõOUT¾úY±ò½%l[?×?[òÇŸ>…\`
|
|
||||||
¨|OÕæÛóóÍ0©ü2çù0sztí7}×׿7@åèdÓFä9ö|’\`
|
|
||||||
\`›ÏÜÔœÁNÅBp3nòsnC±@¢‡o=[++?*׉áëÚ$ýåÞÞÿDZähkVª¨
|
|
||||||
\`°ÏñÞëð XÄ#æQHšCs½‡½zJtj=†}†[†CèܶðëÙÙ$ÚdèÖåÞFáñDhôv›
|
|
||||||
\`^2FÞÙýëððøâããßqŽþÁ#ÁXS3öö@Ìöƒìi=wÙG¶ãéëddëÚéëýÙÜÞÖÓdÞåDPûí…
|
|
||||||
\`’‡áÞë$ë$éé8¶¶ððG¶¶ãßÐÄNÁEþRKgÛÔApmmA€âøÚ$$$ë8¶ð$$éÜýýmëýëÙèFû>·
|
|
||||||
\`/Pè8$$ø€âÜ8Úðø88¶mâ¶ø¶pÛRÄÀHXœÐÛÛÕÕp¶ððéÚ8Ü$ÚðGGÚéøÚëpÜýâ¶ëÞbá§•\`
|
|
||||||
\`÷šådÚø¶ðÚÔqÚ$8¶ðÚðGââ¶ðøð¶ãÔÐqmøðéðððéééÚéé8Ü$éøß8¶¶Ú¶GÜ€âÜëÜÙÿäž¹
|
|
||||||
;µå$ë$ð¶G¶éŠÔÜÚGðÜéðé¶øøðéø¶mpÔÕAââGðøðððÚÜé8Ú8éâ€øãGðß¶ŠßðGðëýÞDš2…
|
|
||||||
\`¿àè$Gâð¶mø¶¶ÐøéøðÚ8éÜðéé88ðøðmmÔÔpâøøéðÚÚðééð8ééGÔâpã¶ãqÛãßâÚÜ$ÞÒ¥Ý*
|
|
||||||
‚ôFëÚðpÔøøÔâGp¶ø¶øÚéðéÚÚÚÚ8ðéÚ8¶mppã¶ð8Ú8Ü8ÚÚÚ8Úð¶Õm€ß〜AÔââ¶Ú$$ÙÓš2…
|
|
||||||
\`[ûÞÜðø¶pÕããKmAãø¶øÚÚÚéÚÚÚÚÚÚðéøéð¶â¶øð8é888Ú8ðéÜ8¶AmÔêAêqŠßAGøãøëýÓ¥e”
|
|
||||||
…Låë$ܶp€€ÛAßÄÔ€ðééééðð8Ú88ÚÚé¶ðø¶ø¶G¶éðððð8Úéé8ééâp€ÔœêHÐÔÐmÕßéÚÚ$ÓFš±‚
|
|
||||||
‹4Ùððé8ÜøÔêÐAâK€ðøø¶¶øøð8éÚÜÚ8Üø¶¶¶ø¶¶ðøéððð8ééðððøpAAœXHŽHAÔ¶éé8ÜdýÿZ®I·
|
|
||||||
—edéððG¶88âÐÀgÔê¶Gãmã¶øøøéðÚÚÜÜéøøø¶øøâ¶éøéðéðøøðø¶m€AXKÀÄÕ€øéøøÜ$$dÖbZú¯
|
|
||||||
\`[ûÜéßâÚðããðÚ¶RÁRmmßßmm¶øéÚÚð8éÜéðø¶âââ¶¶G¶øðé8éøðøGmßÔŽÄNœA¶ãpâé88ÜëÞÿbÝ<\`
|
|
||||||
…sÿÙÜðAÕGÜÚðð88ÔE€p€pppâG¶é888éÚé8ø¶âããâââøøðÚéøø¶¶ãpÔêHþEqããÔâð¶øéÜëdýÖPz’
|
|
||||||
’çÙÙ$88GAßøÚÚéðãgÛAÔÔA€pãG¶¶¶ðð8éøð¶ããããããG¶¶¶ø¶¶mâãAŠgÀÁEÕßÔã$DàÓð88é$ýFU}·
|
|
||||||
²ôÚ¶ÚëÚééømm8ë$GggÔÕqŠÔ€mãG¶G¶øðøø¶¶âmmmmmmâããâããßpßÔÐRþæÁÛ€D±cc‡µ9nj¾8dåZ™^\`
|
|
||||||
¡OÙ¶mGÜÚÚé8ð¶ðÜéÔŽqêgêÛÔApmãâãGãGâ¶ãâmp€ppppp߀ßÔÔÕqÐŽÀNQãõjïI‡¤ííïoJ5Ü8èÒP¼‚
|
|
||||||
(kÙ8¶ÔÔGÚ8ð88ðÚémKgœŽœÐêÛÔ€pmmmmm€ãpp€ßA€ÔÔÔAÔÔÔÔŠqgœHEE¥uvo¼l[[íƒzovJÝð$ýb9^\`
|
|
||||||
´iPÙÚøø€qÔâéðøéðéâHHHŽœgêêÛÕ€pmpßm€ß€€AAÔÔÛÕÕŠÛÛÛŠêgœREŽŸ½‰¢%%î1j<>jr¼ÒGðÜýÒPï·
|
|
||||||
…rÒdðøøGmÔŠ€øéâøðâÄNÀXXXggÐêÕ€pp€AAÔÔAÔÔÔÕŠÛêqÛqqêÐœŽœý0z½‰¤t½ti<tƒr‡ùÖ¶ããøÙFò…
|
|
||||||
ˆƒÓ$é¶âââãâ¶ðÚ$¶ðÖeùÍ2§8ÛêÛm8¶Gâ¶ÔmŠÔ€AÕqŠqÐÐÐÐÐggœœKë½trVó3Y©ùfP$¶ãð$ëÙÞ¥ô˜
|
|
||||||
’¼ýÚé8ððø¶ã€p¶mmÚf1í=í=írjrzÇédááÕmëéÙdéGèýý¶øðãÚ¶AðÙhfõ6eÝÖðâÙdëëé¶ãGâŠêéÒÒFU’
|
|
||||||
\` ‚sÜøGéé €ßÕЊÔÓ½1í=ïìíí=íÍGAZŸðÚÙáãýëøø$Ü$GãëÜ$ëðãëÙé8Ú8ð8éãÔßéøâããmããâmébDk¹
|
|
||||||
\` ’nÜãããpêKEHRXÛßÔ¶w%oí<<<[7=©øÚÙÿhhýgøé¶m¶8ø¶mÔG¶ø$éßmÚÜø¶øðø¶âãÔÕâðGp€Am¶øø$¥h–
|
|
||||||
’öÚã€ÕÕßpmmmâ¶Gðéÿf&Lc=ì<<<‰üÍ©OÚqm¶âppÕã8ðâpÔAmßméðpGÜ$ð¶ãâG¶â¶mÔß¶ø¶ãpã¶ð8Öh°
|
|
||||||
\`\` ’½ÜâAÛgêÔÔApmãmã¶Gøø8äyúüüüaúõúx߀AAÔŠqÐÔð$Ú¶AÔAAÛÔ¶ðøéÚ$Úøâ¶Gâââã€Ap¶øø¶¶éÜèŸ;`]
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<a href="{{url}}" class="mr-2 text-lg px-4 sm:px-12 py-4 rounded-full mt-4 font-bold
|
|
||||||
ring-white text-white bg-secondary ring-2
|
|
||||||
hover:ring-accent hover:text-accent hover:bg-transparent hover:ring-2">
|
|
||||||
<ng-content></ng-content>
|
|
||||||
</a>
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ButtonCtaComponent } from './button-cta.component';
|
|
||||||
|
|
||||||
describe('ButtonCtaComponent', () => {
|
|
||||||
let component: ButtonCtaComponent;
|
|
||||||
let fixture: ComponentFixture<ButtonCtaComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [ButtonCtaComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ButtonCtaComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import {Component, Input} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-button-cta',
|
|
||||||
standalone: true,
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './button-cta.component.html',
|
|
||||||
styleUrl: './button-cta.component.scss'
|
|
||||||
})
|
|
||||||
export class ButtonCtaComponent {
|
|
||||||
@Input() url: string = '';
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<div class="relative bg-clip-border {{borderRounded ? 'rounded-2xl' : 'rounded-0'}} shadow-2xl shadow-accent bg-white text-gray-700 w-full items-center text-center">
|
|
||||||
<div class="grid grid-cols-3 justify-items-center bg-slate-100 p-4">
|
|
||||||
<div class="text-2xl font-bold text-center self-center">
|
|
||||||
<ng-content select="[left]"></ng-content>
|
|
||||||
</div>
|
|
||||||
<div class="self-center">
|
|
||||||
<ng-content select="[center]"></ng-content>
|
|
||||||
</div>
|
|
||||||
<div class="self-center">
|
|
||||||
<iov-button-cta
|
|
||||||
[url]="ctaUrl">
|
|
||||||
<ng-content select="[right]">
|
|
||||||
</ng-content>
|
|
||||||
</iov-button-cta>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { CardCtaComponent } from './card-cta.component';
|
|
||||||
|
|
||||||
describe('CardComponent', () => {
|
|
||||||
let component: CardCtaComponent;
|
|
||||||
let fixture: ComponentFixture<CardCtaComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [CardCtaComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CardCtaComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import {Component, Input} from '@angular/core';
|
|
||||||
import {ButtonCtaComponent} from "../button-cta/button-cta.component";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-card',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
ButtonCtaComponent
|
|
||||||
],
|
|
||||||
templateUrl: './card-cta.component.html',
|
|
||||||
styleUrl: './card-cta.component.scss'
|
|
||||||
})
|
|
||||||
export class CardCtaComponent {
|
|
||||||
|
|
||||||
@Input() color: 'light' | 'dark' = 'light';
|
|
||||||
@Input() ctaUrl: string = '';
|
|
||||||
@Input() borderRounded: boolean = true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<div class="w-full overflow-hidden block">
|
|
||||||
<svg class="fish" id="fish">
|
|
||||||
<path
|
|
||||||
id="fish2"
|
|
||||||
d="m 172.04828,20.913839 c 0.0489,-0.444179 -0.2178,-0.896934 -1.01784,-1.415715 -0.72801,-0.475049 -1.4826,-0.932948 -2.2149,-1.401138 -1.6035,-1.028129 -3.29018,-1.969653 -4.89798,-3.079244 -4.67074,-3.24131 -10.22127,-4.404923 -15.76322,-5.1509392 -2.27235,-0.286401 -4.81223,-0.168925 -6.72186,-1.574351 -1.48174,-1.081294 -4.04993,-4.828523 -6.86506,-6.456038 -0.4862,-0.290688 -2.77227,-1.44486897 -2.77227,-1.44486897 0,0 1.30939,3.55000597 1.60951,4.26429497 0.69542,1.644664 -0.38158,3.063809 -0.83262,4.642447 -0.29069,1.0418502 2.13772,0.8129002 2.26463,1.7827212 0.18179,1.432007 -4.15197,1.936211 -6.59152,2.417263 -3.65634,0.715146 -7.91635,2.082841 -11.56925,0.884071 -4.3046,-1.38313 -7.37269,-4.129669 -10.46566,-7.2354952 1.43801,6.7252892 5.4382,10.6028562 5.6157,11.4226162 0.18607,0.905509 -0.45961,1.091584 -1.04099,1.682394 -1.28967,1.265655 -6.91566,7.731125 -6.93366,9.781383 1.61379,-0.247815 3.56115,-1.660957 4.9803,-2.485862 1.58035,-0.905509 7.60593,-5.373029 9.29347,-6.065023 0.38587,-0.160351 5.0549,-1.531476 5.09434,-0.932949 0.0695,0.932949 -0.30784,1.137031 -0.18436,1.527189 0.22638,0.746016 1.44144,1.465449 2.02282,1.985088 1.50918,1.292237 3.21044,2.42841 4.27373,4.156252 1.49203,2.401827 1.55805,4.999163 1.98251,7.677102 0.99469,-0.111473 2.0091,-2.17545 2.55961,-2.992638 0.51278,-0.772598 2.38639,-4.07136 3.09725,-4.275442 0.67227,-0.204082 2.75511,0.958673 3.50284,1.180763 2.85973,0.848057 5.644,1.353976 8.56032,1.353976 3.50799,0.0094 12.726,0.258104 19.55505,-4.800226 0.75545,-0.567658 2.55703,-2.731104 2.55703,-2.731104 0,0 -0.37644,-0.577091 -1.04785,-0.790605 0.89779,-0.584808 1.8659,-1.211633 1.94993,-1.925922 z"
|
|
||||||
style="fill:#528484;fill-opacity:1"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccc" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="fill:#528484;fill-opacity:1"
|
|
||||||
d="m 234.99441,42.953992 c 0.0489,-0.44418 -0.2178,-0.896934 -1.01784,-1.415715 -0.72801,-0.47505 -1.4826,-0.932949 -2.2149,-1.401138 -1.6035,-1.02813 -3.29018,-1.969653 -4.89798,-3.079245 -4.67074,-3.24131 -10.22127,-4.404923 -15.76322,-5.150939 -2.27235,-0.286401 -4.81223,-0.168925 -6.72186,-1.574351 -1.48174,-1.081294 -4.04993,-4.828523 -6.86506,-6.456038 -0.4862,-0.290688 -2.77227,-1.444869 -2.77227,-1.444869 0,0 1.30939,3.550006 1.60951,4.264295 0.69542,1.644664 -0.38158,3.063809 -0.83262,4.642447 -0.29069,1.04185 2.13772,0.8129 2.26463,1.782721 0.18179,1.432007 -4.15197,1.936211 -6.59152,2.417263 -3.65634,0.715146 -7.91635,2.082842 -11.56925,0.884072 -4.3046,-1.383131 -7.37269,-4.12967 -10.46566,-7.235496 1.43801,6.725289 5.4382,10.602857 5.6157,11.422617 0.18607,0.905508 -0.45961,1.091583 -1.04099,1.682394 -1.28967,1.265654 -6.91566,7.731125 -6.93366,9.781382 1.61379,-0.247815 3.56115,-1.660957 4.9803,-2.485862 1.58035,-0.905509 7.60593,-5.373029 9.29347,-6.065023 0.38587,-0.160351 5.0549,-1.531476 5.09434,-0.932948 0.0695,0.932948 -0.30784,1.137031 -0.18436,1.527188 0.22638,0.746016 1.44144,1.46545 2.02282,1.985088 1.50918,1.292237 3.21044,2.42841 4.27373,4.156252 1.49203,2.401827 1.55805,4.999163 1.98251,7.677102 0.99469,-0.111473 2.0091,-2.17545 2.55961,-2.992638 0.51278,-0.772598 2.38639,-4.071359 3.09725,-4.275442 0.67227,-0.204082 2.75511,0.958673 3.50284,1.180763 2.85973,0.848057 5.644,1.353976 8.56032,1.353976 3.50799,0.0094 12.726,0.258104 19.55505,-4.800226 0.75545,-0.567658 2.55703,-2.731104 2.55703,-2.731104 0,0 -0.37644,-0.57709 -1.04785,-0.790605 0.89779,-0.584808 1.8659,-1.211633 1.94993,-1.925921 z"
|
|
||||||
id="fish3" />
|
|
||||||
<path
|
|
||||||
id="fish6"
|
|
||||||
d="m 200.07076,80.737109 c 0.0489,-0.44418 -0.2178,-0.896934 -1.01784,-1.415715 -0.72801,-0.47505 -1.4826,-0.932949 -2.2149,-1.401138 -1.6035,-1.02813 -3.29018,-1.969653 -4.89798,-3.079245 -4.67074,-3.24131 -10.22127,-4.404923 -15.76322,-5.150939 -2.27235,-0.286401 -4.81223,-0.168925 -6.72186,-1.574351 -1.48174,-1.081294 -4.04993,-4.828523 -6.86506,-6.456038 -0.4862,-0.290688 -2.77227,-1.444869 -2.77227,-1.444869 0,0 1.30939,3.550006 1.60951,4.264295 0.69542,1.644664 -0.38158,3.063809 -0.83262,4.642447 -0.29069,1.04185 2.13772,0.8129 2.26463,1.782721 0.18179,1.432007 -4.15197,1.936211 -6.59152,2.417263 -3.65634,0.715146 -7.91635,2.082842 -11.56925,0.884072 -4.3046,-1.383131 -7.37269,-4.12967 -10.46566,-7.235496 1.43801,6.725289 5.4382,10.602857 5.6157,11.422617 0.18607,0.905508 -0.45961,1.091583 -1.04099,1.682394 -1.28967,1.265654 -6.91566,7.731125 -6.93366,9.781382 1.61379,-0.247815 3.56115,-1.660957 4.9803,-2.485862 1.58035,-0.905509 7.60593,-5.373029 9.29347,-6.065023 0.38587,-0.160351 5.0549,-1.531476 5.09434,-0.932948 0.0695,0.932948 -0.30784,1.137031 -0.18436,1.527188 0.22638,0.746016 1.44144,1.46545 2.02282,1.985088 1.50918,1.292237 3.21044,2.42841 4.27373,4.156252 1.49203,2.401827 1.55805,4.999163 1.98251,7.677102 0.99469,-0.111473 2.0091,-2.17545 2.55961,-2.992638 0.51278,-0.772598 2.38639,-4.071359 3.09725,-4.275442 0.67227,-0.204082 2.75511,0.958673 3.50284,1.180763 2.85973,0.848057 5.644,1.353976 8.56032,1.353976 3.50799,0.0094 12.726,0.258104 19.55505,-4.800226 0.75545,-0.567658 2.55703,-2.731104 2.55703,-2.731104 0,0 -0.37644,-0.57709 -1.04785,-0.790605 0.89779,-0.584808 1.8659,-1.211633 1.94993,-1.925921 z"
|
|
||||||
style="fill:#528484;fill-opacity:1"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccc" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="fill:#528484;fill-opacity:1"
|
|
||||||
d="m 77.275623,89.018799 c 0.0489,-0.44418 -0.2178,-0.896934 -1.01784,-1.415715 -0.72801,-0.47505 -1.4826,-0.932949 -2.2149,-1.401138 -1.6035,-1.02813 -3.29018,-1.969653 -4.89798,-3.079245 -4.67074,-3.24131 -10.22127,-4.404923 -15.76322,-5.150939 -2.272347,-0.286401 -4.812227,-0.168925 -6.721857,-1.574351 -1.48174,-1.081294 -4.04993,-4.828523 -6.86506,-6.456038 -0.4862,-0.290688 -2.77227,-1.444869 -2.77227,-1.444869 0,0 1.30939,3.550006 1.60951,4.264295 0.69542,1.644664 -0.38158,3.063809 -0.83262,4.642447 -0.29069,1.04185 2.13772,0.8129 2.26463,1.782721 0.18179,1.432007 -4.15197,1.936211 -6.59152,2.417263 -3.65634,0.715146 -7.91635,2.082842 -11.56925,0.884072 -4.3046,-1.383131 -7.37269,-4.12967 -10.46566,-7.235496 1.43801,6.725289 5.4382,10.602857 5.6157,11.422617 0.18607,0.905508 -0.45961,1.091583 -1.04099,1.682394 -1.28967,1.265654 -6.9156603,7.731122 -6.9336603,9.781382 1.6137903,-0.24782 3.5611503,-1.66096 4.9803003,-2.48586 1.58035,-0.90551 7.60593,-5.37303 9.29347,-6.065025 0.38587,-0.160351 5.0549,-1.531476 5.09434,-0.932948 0.0695,0.932948 -0.30784,1.137031 -0.18436,1.527183 0.22638,0.74602 1.44144,1.46546 2.02282,1.98509 1.50918,1.29224 3.21044,2.42841 4.27373,4.15625 1.49203,2.40183 1.55805,4.999171 1.98251,7.677111 0.99469,-0.11148 2.0091,-2.17545 2.55961,-2.99264 0.51278,-0.7726 2.38639,-4.071361 3.09725,-4.275441 0.67227,-0.20408 2.75511,0.95867 3.50284,1.18076 2.85973,0.84806 5.644,1.35398 8.560317,1.35398 3.50799,0.009 12.726,0.2581 19.55505,-4.80023 0.75545,-0.56766 2.55703,-2.7311 2.55703,-2.7311 0,0 -0.37644,-0.57709 -1.04785,-0.79061 0.89779,-0.58481 1.8659,-1.211632 1.94993,-1.92592 z"
|
|
||||||
id="fish4" />
|
|
||||||
<path
|
|
||||||
id="fish5"
|
|
||||||
d="m 127.65312,60.900973 c 0.0489,-0.44418 -0.2178,-0.896934 -1.01784,-1.415715 -0.72801,-0.47505 -1.4826,-0.932949 -2.2149,-1.401138 -1.6035,-1.02813 -3.29018,-1.969653 -4.89799,-3.079245 -4.67074,-3.24131 -10.22127,-4.404923 -15.76322,-5.150939 -2.27235,-0.286401 -4.812228,-0.168925 -6.721858,-1.574351 -1.48174,-1.081294 -4.04993,-4.828523 -6.86506,-6.456038 -0.4862,-0.290688 -2.77227,-1.444869 -2.77227,-1.444869 0,0 1.30939,3.550006 1.60951,4.264295 0.69542,1.644664 -0.38158,3.063809 -0.83262,4.642447 -0.29069,1.04185 2.13772,0.8129 2.26463,1.782721 0.18179,1.432007 -4.15197,1.936211 -6.59152,2.417263 -3.65634,0.715146 -7.91635,2.082842 -11.56925,0.884072 -4.3046,-1.383131 -7.37269,-4.12967 -10.46566,-7.235496 1.43801,6.725289 5.4382,10.602857 5.6157,11.422617 0.18607,0.905508 -0.45961,1.091583 -1.04099,1.682394 -1.28967,1.265654 -6.91566,7.731125 -6.93366,9.781382 1.61379,-0.247815 3.56115,-1.660957 4.9803,-2.485862 1.58035,-0.905509 7.60593,-5.373029 9.29347,-6.065023 0.38587,-0.160351 5.0549,-1.531476 5.09434,-0.932948 0.0695,0.932948 -0.30784,1.137031 -0.18436,1.527188 0.22638,0.746016 1.44144,1.46545 2.02282,1.985088 1.50918,1.292237 3.21044,2.42841 4.27373,4.156252 1.49203,2.401827 1.55805,4.999163 1.98251,7.677102 0.99469,-0.111473 2.0091,-2.17545 2.55961,-2.992638 0.51278,-0.772598 2.38639,-4.071359 3.09725,-4.275442 0.67227,-0.204082 2.75511,0.958673 3.50284,1.180763 2.85973,0.848057 5.643998,1.353976 8.560318,1.353976 3.50799,0.0094 12.726,0.258104 19.55506,-4.800226 0.75545,-0.567658 2.55703,-2.731104 2.55703,-2.731104 0,0 -0.37644,-0.57709 -1.04785,-0.790605 0.89779,-0.584808 1.8659,-1.211633 1.94993,-1.925921 z"
|
|
||||||
style="fill:#528484;fill-opacity:1"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccc" />
|
|
||||||
<path
|
|
||||||
d="m 68.19699,20.522295 c 0.0489,-0.44418 -0.2178,-0.896934 -1.01784,-1.415715 -0.72801,-0.47505 -1.4826,-0.932949 -2.2149,-1.401138 -1.6035,-1.02813 -3.29018,-1.969653 -4.89798,-3.079245 C 55.39553,11.384887 49.845,10.221274 44.30305,9.4752582 42.0307,9.1888572 39.49082,9.3063332 37.58119,7.900907 36.09945,6.819613 33.53126,3.072384 30.71613,1.444869 30.22993,1.154181 27.94386,0 27.94386,0 c 0,0 1.30939,3.550006 1.60951,4.264295 0.69542,1.644664 -0.38158,3.063809 -0.83262,4.642447 -0.29069,1.0418502 2.13772,0.8129002 2.26463,1.782721 0.18179,1.432007 -4.15197,1.936211 -6.59152,2.417263 -3.65634,0.715146 -7.91635,2.082842 -11.56925,0.884072 C 8.52001,12.607667 5.45192,9.8611282 2.35895,6.755302 3.79696,13.480591 7.79715,17.358159 7.97465,18.177919 8.16072,19.083427 7.51504,19.269502 6.93366,19.860313 5.64399,21.125967 0.018,27.591438 0,29.641695 1.61379,29.39388 3.56115,27.980738 4.9803,27.155833 c 1.58035,-0.905509 7.60593,-5.373029 9.29347,-6.065023 0.38587,-0.160351 5.0549,-1.531476 5.09434,-0.932948 0.0695,0.932948 -0.30784,1.137031 -0.18436,1.527188 0.22638,0.746016 1.44144,1.46545 2.02282,1.985088 1.50918,1.292237 3.21044,2.42841 4.27373,4.156252 1.49203,2.401827 1.55805,4.999163 1.98251,7.677102 0.99469,-0.111473 2.0091,-2.17545 2.55961,-2.992638 0.51278,-0.772598 2.38639,-4.071359 3.09725,-4.275442 0.67227,-0.204082 2.75511,0.958673 3.50284,1.180763 2.85973,0.848057 5.644,1.353976 8.56032,1.353976 3.50799,0.0094 12.726,0.258104 19.55505,-4.800226 0.75545,-0.567658 2.55703,-2.731104 2.55703,-2.731104 0,0 -0.37644,-0.57709 -1.04785,-0.790605 0.89779,-0.584808 1.8659,-1.211633 1.94993,-1.925921 z"
|
|
||||||
id="fish1" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
svg#fish {
|
|
||||||
}
|
|
||||||
/* Fish Animation */
|
|
||||||
svg.fish{
|
|
||||||
overflow:visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes swim
|
|
||||||
{
|
|
||||||
0% {left: -235px}
|
|
||||||
90% {left: calc(100% - 235px); width: 235px;}
|
|
||||||
100% {left: calc(100%); width: 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes swim
|
|
||||||
{
|
|
||||||
0% {left: -235px}
|
|
||||||
70% {left: calc(100% - 235px); width: 235px;}
|
|
||||||
100% {left: calc(100%); width: 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fish{
|
|
||||||
width: 235px;
|
|
||||||
height: 104px;
|
|
||||||
left: -235px;
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0.4;
|
|
||||||
animation: swim 20s;
|
|
||||||
-webkit-animation: swim 20s;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
-webkit-animation-iteration-count: infinite;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
-webkit-animation-timing-function: linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #fish1,
|
|
||||||
svg #fish2,
|
|
||||||
svg #fish3,
|
|
||||||
svg #fish4,
|
|
||||||
svg #fish5,
|
|
||||||
svg #fish6 {
|
|
||||||
fill:#528484;
|
|
||||||
|
|
||||||
-moz-animation: bounce 2s infinite;
|
|
||||||
-webkit-animation: bounce 2s infinite;
|
|
||||||
animation: bounce 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #fish2{
|
|
||||||
animation-delay: 0.5s;
|
|
||||||
-webkit-animation-delay: 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #fish3{
|
|
||||||
animation-delay: 0.2s;
|
|
||||||
-webkit-animation-delay: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #fish4{
|
|
||||||
animation-delay: 0.4s;
|
|
||||||
-webkit-animation-delay: 0.4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #fish5{
|
|
||||||
animation-delay: 0.1s;
|
|
||||||
-webkit-animation-delay: 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #fish6{
|
|
||||||
animation-delay: 0.3s;
|
|
||||||
-webkit-animation-delay: 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**/
|
|
||||||
@-moz-keyframes bounce {
|
|
||||||
0%, 50%, 100% {
|
|
||||||
-moz-transform: translateY(0);
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
-moz-transform: translateY(-5px);
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
-moz-transform: translateY(-3px);
|
|
||||||
transform: translateY(-3px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes bounce {
|
|
||||||
0%, 50%, 100% {
|
|
||||||
-webkit-transform: translateY(0);
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
-webkit-transform: translateY(-5px);
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
-webkit-transform: translateY(-3px);
|
|
||||||
transform: translateY(-3px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes bounce {
|
|
||||||
0%, 50%, 100% {
|
|
||||||
-moz-transform: translateY(0);
|
|
||||||
-ms-transform: translateY(0);
|
|
||||||
-webkit-transform: translateY(0);
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
-moz-transform: translateY(-5px);
|
|
||||||
-ms-transform: translateY(-5px);
|
|
||||||
-webkit-transform: translateY(-5px);
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
-moz-transform: translateY(-3px);
|
|
||||||
-ms-transform: translateY(-3px);
|
|
||||||
-webkit-transform: translateY(-3px);
|
|
||||||
transform: translateY(-3px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { FishComponent } from './fish.component';
|
|
||||||
|
|
||||||
describe('FishComponent', () => {
|
|
||||||
let component: FishComponent;
|
|
||||||
let fixture: ComponentFixture<FishComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [FishComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(FishComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-fish',
|
|
||||||
standalone: true,
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './fish.component.html',
|
|
||||||
styleUrl: './fish.component.scss'
|
|
||||||
})
|
|
||||||
export class FishComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<div class="bottom-0 bg-secondary pt-12 w-full h-full grid grid-cols-1 content-end justify-end bg-slate-100 gap-8">
|
|
||||||
<iov-fish></iov-fish>
|
|
||||||
<iov-card [ctaUrl]="'mailto:thisloke@gmail.com'"
|
|
||||||
[borderRounded]="false"
|
|
||||||
class="mr-8 ml-8 relative -top-12 hidden">
|
|
||||||
<span left>
|
|
||||||
<span class="text-accent"> Talk is cheap</span>
|
|
||||||
</span>
|
|
||||||
<span center>
|
|
||||||
Interested in working together? <br/> We should schedule a <b class="text-accent">time to chat</b>. <br/>I’ll bring the tea :)
|
|
||||||
</span>
|
|
||||||
<span right>
|
|
||||||
Let's Talk!
|
|
||||||
</span>
|
|
||||||
</iov-card>
|
|
||||||
<div class="flex justify-between self-end flex-col-reverse sm:flex-row">
|
|
||||||
<div class="text-center sm:text-left text-white p-0 sm:p-4 leading-none sm:leading-tight flex flex-col z-10 text-sm sm:text-sm justify-end items-center sm:items-start">
|
|
||||||
<div class="flex my-2 flex-wrap w-max">
|
|
||||||
<div class="mx-2">
|
|
||||||
<span class="italic mr-1">Lorenzo Iovino</span> |
|
|
||||||
</div>
|
|
||||||
<a href="https://www.iubenda.com/privacy-policy/98687046" class="hover:text-accent iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe" title="Privacy Policy ">Privacy Policy</a><script type="text/javascript">(function (w,d) {var loader = function () {var s = d.createElement("script"), tag = d.getElementsByTagName("script")[0]; s.src="https://cdn.iubenda.com/iubenda.js"; tag.parentNode.insertBefore(s,tag);}; if(w.addEventListener){w.addEventListener("load", loader, false);}else if(w.attachEvent){w.attachEvent("onload", loader);}else{w.onload = loader;}})(window, document);</script>
|
|
||||||
<a href="https://www.iubenda.com/privacy-policy/98687046/cookie-policy" class="ml-2 hover:text-accent iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe " title="Cookie Policy ">Cookie Policy</a><script type="text/javascript">(function (w,d) {var loader = function () {var s = d.createElement("script"), tag = d.getElementsByTagName("script")[0]; s.src="https://cdn.iubenda.com/iubenda.js"; tag.parentNode.insertBefore(s,tag);}; if(w.addEventListener){w.addEventListener("load", loader, false);}else if(w.attachEvent){w.attachEvent("onload", loader);}else{w.onload = loader;}})(window, document);</script>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-center sm:text-right text-white p-0 sm:p-4 leading-none sm:leading-tight relative mx-2 text-sm sm:text-sm pb-4 sm:mb-0">
|
|
||||||
<div class="">Made with <span class="hidden sm:inline">Angular</span>
|
|
||||||
<a href="https://angular.io/">
|
|
||||||
<img class="h-6 w-6 mx-1 inline-grid" ngSrc="/assets/angular.svg" height="16" width="16" loading="lazy" alt="Angular Logo">
|
|
||||||
</a>
|
|
||||||
and <span class="hidden sm:inline">TailwindCSS</span>
|
|
||||||
<a href="https://tailwindcss.com/">
|
|
||||||
<img class="h-6 w-6 mx-1 inline-grid" ngSrc="/assets/tailwind.svg" height="16" width="16" loading="lazy" alt="Tailwind Logo">
|
|
||||||
</a>
|
|
||||||
<br class=""/>
|
|
||||||
<a href="https://github.com/thisloke/loreiov.com" class="hover:text-accent">
|
|
||||||
Check the source code <img ngSrc="/assets/github.svg" class="h-6 w-6 mx-1 my-1 inline-block" height="800"
|
|
||||||
width="800" loading="lazy" alt="Github Logo"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
:host-context {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { FooterComponent } from './footer.component';
|
|
||||||
|
|
||||||
describe('FooterComponent', () => {
|
|
||||||
let component: FooterComponent;
|
|
||||||
let fixture: ComponentFixture<FooterComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [FooterComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(FooterComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import {FishComponent} from "../fish/fish.component";
|
|
||||||
import {CardCtaComponent} from "../card-cta/card-cta.component";
|
|
||||||
import {NgOptimizedImage} from "@angular/common";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-footer',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
CardCtaComponent,
|
|
||||||
FishComponent,
|
|
||||||
NgOptimizedImage
|
|
||||||
],
|
|
||||||
templateUrl: './footer.component.html',
|
|
||||||
styleUrl: './footer.component.scss'
|
|
||||||
})
|
|
||||||
export class FooterComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
<div class="bg-secondary">
|
|
||||||
<div class="relative px-6 lg:px-8">
|
|
||||||
<div class="mx-auto max-w-6xl">
|
|
||||||
<div class="flex rounded-2xl text-base leading-6 text-gray-600 border-l-8 border-primary hover:ring-gray-900/20 overflow-hidden bg-white items-end">
|
|
||||||
<iov-ascii-photo [index]="1"
|
|
||||||
id="asciiPhoto"
|
|
||||||
class="hidden sm:block absolute text-[5px] rounded-x-2xl leading-none tracking-tighter overflow-hidden"></iov-ascii-photo>
|
|
||||||
<img ngSrc="/assets/photos/me.png"
|
|
||||||
id="originalPhoto"
|
|
||||||
priority="true"
|
|
||||||
rel="preload"
|
|
||||||
class="h-[279px] hidden sm:block "
|
|
||||||
alt="Photo of me" height="300" width="300">
|
|
||||||
<div class="self-center pt-6 sm:pt-0 mx-2 sm:mx-0 ">
|
|
||||||
<h1 class="text-4xl font-bold text-gray-900 sm:text-5xl sm:py-8 sm:top-8 mt-4 sm:mt-0 mb-2 sm:mb-0 break-words block sm:hidden mx-2">Hey, I'm Lorenzo!</h1>
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<img ngSrc="/assets/photos/me.png"
|
|
||||||
priority="true"
|
|
||||||
rel="preload"
|
|
||||||
class="sm:hidden block clear-both"
|
|
||||||
alt="Photo of me" height="300" width="300">
|
|
||||||
</div>
|
|
||||||
<h1 class="text-4xl font-bold text-gray-900 sm:text-5xl sm:py-8 sm:top-8 mt-4 sm:mt-0 mb-2 sm:mb-0 break-words hidden sm:block">Hey, I'm Lorenzo!</h1>
|
|
||||||
<div class="mx-auto sm:mr-8">
|
|
||||||
<h2 class="text-lg mr-4 ml-4 sm:ml-0 sm:mx-4 sm:text-xl leading-2 sm:mr-24 mt-4 sm:mt-0 text-left">
|
|
||||||
I'm on a quest to uncover life's meaning while diving deep into my passion for Computer Science as a
|
|
||||||
<span class="text-primary font-bold drop-shadow-sm">Software Engineer</span>.
|
|
||||||
<br>Here, on my personal website, I share my projects and occasional thoughts.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div class="text-lg mt-4 ml-4 sm:ml-0 mr-4 sm:mx-4 sm:text-xl leading-2 sm:mr-24 text-left">
|
|
||||||
Explore more about me and feel free to reach out for any questions or collaborations via
|
|
||||||
<a class="cursor-pointer underline text-primary hover:text-accent drop-shadow-sm" href="mailto:thisloke@gmail.com">email</a>
|
|
||||||
or on socials!
|
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center sm:justify-end h-[60px] mt-4">
|
|
||||||
<a class="mx-2 relative cursor-pointer" href="https://www.linkedin.com/in/lorenzoiovino/">
|
|
||||||
<img ngSrc="/assets/linkedin.svg" class="h-10 sm:h-8 inline-block" height="30" width="30" loading="lazy" alt="Linkedin Logo">
|
|
||||||
</a>
|
|
||||||
<a class="mx-2 relative cursor-pointer" href="
|
|
||||||
https://github.com/thisloke">
|
|
||||||
<img ngSrc="/assets/github.svg" class="h-10 sm:h-8 inline-block" height="30" width="30" loading="lazy" alt="Github Logo">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
@keyframes blink {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink-inverted {
|
|
||||||
from {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#asciiPhoto {
|
|
||||||
opacity: 0;
|
|
||||||
animation: blink-inverted 2s 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#originalPhoto {
|
|
||||||
opacity: 1;
|
|
||||||
animation: blink 2s 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { HeroComponent } from './hero.component';
|
|
||||||
|
|
||||||
describe('HeroComponent', () => {
|
|
||||||
let component: HeroComponent;
|
|
||||||
let fixture: ComponentFixture<HeroComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [HeroComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(HeroComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import {Component} from '@angular/core';
|
|
||||||
import {AsciiPhotoComponent} from "../ascii-photo/ascii-photo.component";
|
|
||||||
import {AsyncPipe, NgClass, NgIf, NgOptimizedImage} from "@angular/common";
|
|
||||||
import {Ng2FittextModule} from "ng2-fittext";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-hero',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
AsciiPhotoComponent,
|
|
||||||
NgIf,
|
|
||||||
NgClass,
|
|
||||||
AsyncPipe,
|
|
||||||
NgOptimizedImage,
|
|
||||||
Ng2FittextModule,
|
|
||||||
],
|
|
||||||
templateUrl: './hero.component.html',
|
|
||||||
styleUrl: './hero.component.scss'
|
|
||||||
})
|
|
||||||
export class HeroComponent {
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<iov-section [noHeight]="true">
|
|
||||||
<h2 class="text-2xl font-bold my-8" *ngIf="posts.length > 0">Latest blog post</h2>
|
|
||||||
<div class="grid grid-cols-3 justify-items-center bg-slate-100 gap-8">
|
|
||||||
<div *ngFor="let post of posts.slice(0,3)">
|
|
||||||
<iov-post-card [post]="post">
|
|
||||||
</iov-post-card>
|
|
||||||
</div>
|
|
||||||
<a class="font-bold text-primary ring-secondary border-l-8 px-2" routerLink="/blog">View all posts</a>
|
|
||||||
</div>
|
|
||||||
<div class="text-center" *ngIf="posts.length === 0">
|
|
||||||
<p class="text-base">No posts yet.</p>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</iov-section>
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { HighlightComponent } from './highlight.component';
|
|
||||||
|
|
||||||
describe('HighlightComponent', () => {
|
|
||||||
let component: HighlightComponent;
|
|
||||||
let fixture: ComponentFixture<HighlightComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [HighlightComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(HighlightComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import {SectionComponent} from "../section/section.component";
|
|
||||||
import {Post} from "../models/post";
|
|
||||||
import {PostCardComponent} from "../post-card/post-card.component";
|
|
||||||
import {NgForOf, NgIf} from "@angular/common";
|
|
||||||
import {RouterLink} from "@angular/router";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'iov-highlight',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
SectionComponent,
|
|
||||||
PostCardComponent,
|
|
||||||
NgForOf,
|
|
||||||
NgIf,
|
|
||||||
RouterLink
|
|
||||||
],
|
|
||||||
templateUrl: './highlight.component.html',
|
|
||||||
styleUrl: './highlight.component.scss'
|
|
||||||
})
|
|
||||||
export class HighlightComponent {
|
|
||||||
posts: Post[] = [
|
|
||||||
{
|
|
||||||
title: 'Post 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
content: 'Content 1',
|
|
||||||
date: new Date(),
|
|
||||||
readTime: 1,
|
|
||||||
slug: 'post-1',
|
|
||||||
image: 'https://picsum.photos/300/200'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Post 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
content: 'Content 1',
|
|
||||||
date: new Date(),
|
|
||||||
readTime: 1,
|
|
||||||
slug: 'post-1',
|
|
||||||
image: 'https://picsum.photos/300/200'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Post 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
content: 'Content 1',
|
|
||||||
date: new Date(),
|
|
||||||
readTime: 1,
|
|
||||||
slug: 'post-1',
|
|
||||||
image: 'https://picsum.photos/300/200'
|
|
||||||
},{
|
|
||||||
title: 'Post 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
content: 'Content 1',
|
|
||||||
date: new Date(),
|
|
||||||
readTime: 1,
|
|
||||||
slug: 'post-1',
|
|
||||||
image: 'https://picsum.photos/300/200'
|
|
||||||
},{
|
|
||||||
title: 'Post 1',
|
|
||||||
description: 'Description 1',
|
|
||||||
content: 'Content 1',
|
|
||||||
date: new Date(),
|
|
||||||
readTime: 1,
|
|
||||||
slug: 'post-1',
|
|
||||||
image: 'https://picsum.photos/300/200'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||