diff --git a/backend/pb_migrations/1776528654_created_reviews.js b/backend/pb_migrations/1776528654_created_reviews.js
new file mode 100644
index 0000000..83036ea
--- /dev/null
+++ b/backend/pb_migrations/1776528654_created_reviews.js
@@ -0,0 +1,70 @@
+///
+migrate((app) => {
+ const collection = new Collection({
+ "createRule": null,
+ "deleteRule": null,
+ "fields": [
+ {
+ "autogeneratePattern": "[a-z0-9]{15}",
+ "hidden": false,
+ "id": "text3208210256",
+ "max": 15,
+ "min": 15,
+ "name": "id",
+ "pattern": "^[a-z0-9]+$",
+ "presentable": false,
+ "primaryKey": true,
+ "required": true,
+ "system": true,
+ "type": "text"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2375276105",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "hidden": false,
+ "id": "autodate2990389176",
+ "name": "created",
+ "onCreate": true,
+ "onUpdate": false,
+ "presentable": false,
+ "system": false,
+ "type": "autodate"
+ },
+ {
+ "hidden": false,
+ "id": "autodate3332085495",
+ "name": "updated",
+ "onCreate": true,
+ "onUpdate": true,
+ "presentable": false,
+ "system": false,
+ "type": "autodate"
+ }
+ ],
+ "id": "pbc_4163081445",
+ "indexes": [],
+ "listRule": null,
+ "name": "reviews",
+ "system": false,
+ "type": "base",
+ "updateRule": null,
+ "viewRule": null
+ });
+
+ return app.save(collection);
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445");
+
+ return app.delete(collection);
+})
diff --git a/backend/pb_migrations/1776528667_updated_reviews.js b/backend/pb_migrations/1776528667_updated_reviews.js
new file mode 100644
index 0000000..62b5912
--- /dev/null
+++ b/backend/pb_migrations/1776528667_updated_reviews.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(2, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text1579384326",
+ "max": 0,
+ "min": 0,
+ "name": "name",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("text1579384326")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776528682_updated_reviews.js b/backend/pb_migrations/1776528682_updated_reviews.js
new file mode 100644
index 0000000..48fcc99
--- /dev/null
+++ b/backend/pb_migrations/1776528682_updated_reviews.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(3, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text3883309839",
+ "max": 0,
+ "min": 0,
+ "name": "surname",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("text3883309839")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776528713_updated_reviews.js b/backend/pb_migrations/1776528713_updated_reviews.js
new file mode 100644
index 0000000..f4d6d28
--- /dev/null
+++ b/backend/pb_migrations/1776528713_updated_reviews.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(4, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text999008199",
+ "max": 0,
+ "min": 0,
+ "name": "text",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("text999008199")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776528727_updated_reviews.js b/backend/pb_migrations/1776528727_updated_reviews.js
new file mode 100644
index 0000000..264a4ab
--- /dev/null
+++ b/backend/pb_migrations/1776528727_updated_reviews.js
@@ -0,0 +1,27 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(5, new Field({
+ "hidden": false,
+ "id": "number3632866850",
+ "max": null,
+ "min": null,
+ "name": "rating",
+ "onlyInt": false,
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "number"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("number3632866850")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776528770_updated_reviews.js b/backend/pb_migrations/1776528770_updated_reviews.js
new file mode 100644
index 0000000..b071f9f
--- /dev/null
+++ b/backend/pb_migrations/1776528770_updated_reviews.js
@@ -0,0 +1,30 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(6, new Field({
+ "hidden": false,
+ "id": "select2063623452",
+ "maxSelect": 1,
+ "name": "status",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "pending",
+ "published",
+ "spam"
+ ]
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("select2063623452")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776528788_updated_reviews.js b/backend/pb_migrations/1776528788_updated_reviews.js
new file mode 100644
index 0000000..8631f27
--- /dev/null
+++ b/backend/pb_migrations/1776528788_updated_reviews.js
@@ -0,0 +1,27 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(7, new Field({
+ "hidden": false,
+ "id": "number1696582880",
+ "max": null,
+ "min": null,
+ "name": "votesCount",
+ "onlyInt": false,
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "number"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("number1696582880")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776529504_updated_reviews.js b/backend/pb_migrations/1776529504_updated_reviews.js
new file mode 100644
index 0000000..a2f8135
--- /dev/null
+++ b/backend/pb_migrations/1776529504_updated_reviews.js
@@ -0,0 +1,26 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // update collection data
+ unmarshal({
+ "createRule": "",
+ "listRule": "",
+ "updateRule": "",
+ "viewRule": ""
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // update collection data
+ unmarshal({
+ "createRule": null,
+ "listRule": null,
+ "updateRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776529637_updated_reviews.js b/backend/pb_migrations/1776529637_updated_reviews.js
new file mode 100644
index 0000000..91962ce
--- /dev/null
+++ b/backend/pb_migrations/1776529637_updated_reviews.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // add field
+ collection.fields.addAt(8, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text3130199401",
+ "max": 0,
+ "min": 0,
+ "name": "profession",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_4163081445")
+
+ // remove field
+ collection.fields.removeById("text3130199401")
+
+ return app.save(collection)
+})
diff --git a/frontend/src/components/home/Hero.astro b/frontend/src/components/home/Hero.astro
new file mode 100644
index 0000000..a54c50d
--- /dev/null
+++ b/frontend/src/components/home/Hero.astro
@@ -0,0 +1,510 @@
+---
+import Button from '@components/base/Button.astro';
+import { Icon } from 'astro-icon/components';
+
+interface HeroProps {
+ badgeText: string;
+ titleWhite: string;
+ titleGold: string;
+ description: string;
+ btnText?: string;
+ btnHref?: string;
+ btnSecondary?: string;
+ btnSecondaryHref?: string;
+ btnSecondaryClass?: string;
+ modalTarget?: string;
+ bgImage?: string;
+ minHeight?: string;
+ headerOffset?: string;
+ layout?: 'default' | 'with-image';
+ sideImage?: string;
+ sideImageAlt?: string;
+ experienceBadge?: {
+ number: string;
+ text: string;
+ };
+ icon?: string;
+}
+
+const {
+ badgeText,
+ titleWhite,
+ titleGold,
+ description,
+ btnText,
+ btnHref = "#contact",
+ btnSecondary,
+ btnSecondaryHref = "/services",
+ btnSecondaryClass = "",
+ modalTarget,
+ bgImage = "",
+ minHeight = "100vh",
+ headerOffset = "80px",
+ layout = "default",
+ sideImage = "",
+ sideImageAlt = "",
+ experienceBadge,
+ icon
+} = Astro.props as HeroProps;
+
+const showImage = layout === 'with-image' && sideImage;
+---
+
+
+
+
+
+
+ {bgImage && (
+
+ )}
+
+
+
+
+
+
+ {icon ? (
+
+
+
+ ) : (
+
+ )}
+ {badgeText}
+
+
+
+ {titleWhite}
+
+ {titleGold}
+
+
+
{description}
+
+ {btnText && (
+
+ {modalTarget ? (
+
+ {btnText}
+
+ ) : (
+
+ {btnText}
+
+ )}
+ {btnSecondary && (
+
+ {btnSecondary}
+
+ )}
+
+ )}
+
+
+ {showImage && (
+
+
+
+ {experienceBadge && (
+
+ {experienceBadge.number}
+ {experienceBadge.text}
+
+ )}
+
+
+ )}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/layout/header/Header.astro b/frontend/src/components/layout/header/Header.astro
index 973c0f9..2022431 100644
--- a/frontend/src/components/layout/header/Header.astro
+++ b/frontend/src/components/layout/header/Header.astro
@@ -45,10 +45,9 @@ import { COMPANY } from "@constants";
-
-
+
*:not(.mobile-actions) {
display: none;
}
}
diff --git a/frontend/src/components/reviews/ReviewCard.astro b/frontend/src/components/reviews/ReviewCard.astro
index 8c2ca70..d1449d1 100644
--- a/frontend/src/components/reviews/ReviewCard.astro
+++ b/frontend/src/components/reviews/ReviewCard.astro
@@ -3,7 +3,7 @@ import RatingStars from './RatingStars.astro';
export interface Props {
name: string;
- car: string;
+ profession: string;
text: string;
rating: number;
initial: string;
@@ -15,7 +15,7 @@ export interface Props {
const {
name,
- car,
+ profession,
text,
rating,
initial,
@@ -45,7 +45,7 @@ const formatDate = (dateStr: string) => {
{name}
-
{car}
+
{profession}
{formatDate(date)}
@@ -143,7 +143,7 @@ const formatDate = (dateStr: string) => {
margin: 0;
}
- .author-car {
+ .author-profession {
color: #64748b;
font-size: 0.875rem;
margin: 0;
diff --git a/frontend/src/components/reviews/ReviewForm.tsx b/frontend/src/components/reviews/ReviewForm.tsx
new file mode 100644
index 0000000..6594e28
--- /dev/null
+++ b/frontend/src/components/reviews/ReviewForm.tsx
@@ -0,0 +1,397 @@
+import { createSignal, Show, For, createEffect } from "solid-js";
+
+interface ReviewFormProps {
+ onSubmit: (data: {
+ name: string;
+ surname: string;
+ profession: string;
+ rating: number;
+ text: string;
+ }) => void;
+ onCancel?: () => void;
+ user?: {
+ name: string;
+ email: string;
+ avatar?: string;
+ };
+}
+
+const EMOJIS = [
+ "👍", "👎", "❤️", "😊", "😂", "🎉", "🔥", "👏",
+ "😢", "😮", "😡", "🙏", "⭐", "💯", "❤️🔥", "🤔",
+ "👀", "💪", "🚀", "✨"
+];
+
+const DANGEROUS_PATTERNS = [
+ /