first commit
This commit is contained in:
commit
ebd8286e75
1
.env.development
Normal file
1
.env.development
Normal file
@ -0,0 +1 @@
|
||||
EMAIL_SENDER="p3n4lv3r@gmail.com"
|
||||
1
.env.production
Normal file
1
.env.production
Normal file
@ -0,0 +1 @@
|
||||
EMAIL_SENDER="p3n4lv3r@gmail.com"
|
||||
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
dist
|
||||
.netlify
|
||||
|
||||
.DS_Store
|
||||
.cache
|
||||
.env
|
||||
.vercel
|
||||
.output
|
||||
.vinxi
|
||||
|
||||
/build/
|
||||
/api/
|
||||
/server/build
|
||||
/public/build
|
||||
.vinxi
|
||||
# Sentry Config File
|
||||
.env.sentry-build-plugin
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
**/build
|
||||
**/public
|
||||
pnpm-lock.yaml
|
||||
routeTree.gen.ts
|
||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"files.watcherExclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"files.readonlyInclude": {
|
||||
"**/routeTree.gen.ts": true
|
||||
}
|
||||
}
|
||||
72
README.md
Normal file
72
README.md
Normal file
@ -0,0 +1,72 @@
|
||||
# Welcome to TanStack.com!
|
||||
|
||||
This site is built with TanStack Router!
|
||||
|
||||
- [TanStack Router Docs](https://tanstack.com/router)
|
||||
|
||||
It's deployed automagically with Netlify!
|
||||
|
||||
- [Netlify](https://netlify.com/)
|
||||
|
||||
## Development
|
||||
|
||||
From your terminal:
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
This starts your app in development mode, rebuilding assets on file changes.
|
||||
|
||||
## Editing and previewing the docs of TanStack projects locally
|
||||
|
||||
The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app.
|
||||
In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system.
|
||||
|
||||
Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally :
|
||||
|
||||
1. Create a new directory called `tanstack`.
|
||||
|
||||
```sh
|
||||
mkdir tanstack
|
||||
```
|
||||
|
||||
2. Enter the directory and clone this repo and the repo of the project there.
|
||||
|
||||
```sh
|
||||
cd tanstack
|
||||
git clone git@github.com:TanStack/tanstack.com.git
|
||||
git clone git@github.com:TanStack/form.git
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Your `tanstack` directory should look like this:
|
||||
>
|
||||
> ```
|
||||
> tanstack/
|
||||
> |
|
||||
> +-- form/
|
||||
> |
|
||||
> +-- tanstack.com/
|
||||
> ```
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found.
|
||||
|
||||
3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode:
|
||||
|
||||
```sh
|
||||
cd tanstack.com
|
||||
pnpm i
|
||||
# The app will run on https://localhost:3000 by default
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`.
|
||||
|
||||
> [!NOTE]
|
||||
> The updated pages need to be manually reloaded in the browser.
|
||||
|
||||
> [!WARNING]
|
||||
> You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page!
|
||||
26
app.config.ts
Normal file
26
app.config.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { defineConfig } from "@tanstack/react-start/config";
|
||||
import tsConfigPaths from "vite-tsconfig-paths";
|
||||
import { sitemap } from "./src/utils/sitemap";
|
||||
import { generateSitemap } from "tanstack-router-sitemap";
|
||||
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
preset: "netlify",
|
||||
prerender: {
|
||||
routes: ["/", "/seguros", "/formulario"],
|
||||
crawlLinks: true,
|
||||
},
|
||||
},
|
||||
tsr: {
|
||||
appDirectory: "src",
|
||||
},
|
||||
vite: {
|
||||
plugins: [
|
||||
tsConfigPaths({
|
||||
projects: ["./tsconfig.json"],
|
||||
}),
|
||||
generateSitemap(sitemap)
|
||||
],
|
||||
},
|
||||
});
|
||||
12
jsrepo.json
Normal file
12
jsrepo.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/jsrepo@1.47.1/schemas/project-config.json",
|
||||
"repos": ["https://reactbits.dev/ts/tailwind"],
|
||||
"includeTests": false,
|
||||
"watermark": true,
|
||||
"formatter": "prettier",
|
||||
"configFiles": {},
|
||||
"paths": {
|
||||
"*": "./src/blocks",
|
||||
"TextAnimations": "./src/TextAnimations"
|
||||
}
|
||||
}
|
||||
89
package.json
Normal file
89
package.json
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "tanstack-start-example-basic",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vinxi dev",
|
||||
"build": "vinxi build",
|
||||
"start": "vinxi start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/accordion": "^2.2.13",
|
||||
"@heroui/alert": "^2.2.16",
|
||||
"@heroui/autocomplete": "^2.3.17",
|
||||
"@heroui/avatar": "^2.2.12",
|
||||
"@heroui/badge": "^2.2.10",
|
||||
"@heroui/breadcrumbs": "^2.2.12",
|
||||
"@heroui/button": "^2.2.16",
|
||||
"@heroui/calendar": "^2.2.16",
|
||||
"@heroui/card": "^2.2.15",
|
||||
"@heroui/checkbox": "^2.3.15",
|
||||
"@heroui/chip": "^2.2.12",
|
||||
"@heroui/code": "^2.2.12",
|
||||
"@heroui/date-input": "^2.3.15",
|
||||
"@heroui/date-picker": "^2.3.16",
|
||||
"@heroui/divider": "^2.2.11",
|
||||
"@heroui/drawer": "^2.2.13",
|
||||
"@heroui/dropdown": "^2.3.16",
|
||||
"@heroui/form": "^2.1.15",
|
||||
"@heroui/image": "^2.2.10",
|
||||
"@heroui/input": "^2.4.16",
|
||||
"@heroui/input-otp": "^2.1.15",
|
||||
"@heroui/kbd": "^2.2.12",
|
||||
"@heroui/link": "^2.2.13",
|
||||
"@heroui/listbox": "^2.3.15",
|
||||
"@heroui/menu": "^2.2.15",
|
||||
"@heroui/modal": "^2.2.13",
|
||||
"@heroui/navbar": "^2.2.14",
|
||||
"@heroui/pagination": "^2.2.14",
|
||||
"@heroui/popover": "^2.3.16",
|
||||
"@heroui/progress": "^2.2.12",
|
||||
"@heroui/radio": "^2.3.15",
|
||||
"@heroui/ripple": "^2.2.12",
|
||||
"@heroui/scroll-shadow": "^2.3.10",
|
||||
"@heroui/select": "^2.4.16",
|
||||
"@heroui/skeleton": "^2.2.10",
|
||||
"@heroui/slider": "^2.4.13",
|
||||
"@heroui/snippet": "^2.2.17",
|
||||
"@heroui/spinner": "^2.2.13",
|
||||
"@heroui/system": "^2.4.12",
|
||||
"@heroui/table": "^2.2.15",
|
||||
"@heroui/tabs": "^2.2.13",
|
||||
"@heroui/theme": "^2.4.12",
|
||||
"@heroui/tooltip": "^2.2.13",
|
||||
"@heroui/user": "^2.2.12",
|
||||
"@iconify-json/cib": "^1.2.2",
|
||||
"@iconify-json/solar": "^1.2.2",
|
||||
"@iconify/tailwind": "^1.2.0",
|
||||
"@microsoft/clarity": "^1.0.0",
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@tanstack/react-query-devtools": "^5.69.0",
|
||||
"@tanstack/react-router": "^1.114.22",
|
||||
"@tanstack/react-router-devtools": "^1.114.22",
|
||||
"@tanstack/react-router-with-query": "^1.114.25",
|
||||
"@tanstack/react-start": "^1.114.22",
|
||||
"@tanstack/zod-adapter": "^1.114.34",
|
||||
"axios": "^1.8.3",
|
||||
"framer-motion": "^11.18.2",
|
||||
"react": "^19.0.0",
|
||||
"react-cookie": "^8.0.1",
|
||||
"react-dom": "^19.0.0",
|
||||
"redaxios": "^0.5.1",
|
||||
"sonner": "^2.0.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"vinxi": "0.5.3",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.5.4",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tanstack-router-sitemap": "^1.0.12",
|
||||
"typescript": "^5.7.2",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
}
|
||||
}
|
||||
6
postcss.config.mjs
Normal file
6
postcss.config.mjs
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
public/conocenos.webp
Normal file
BIN
public/conocenos.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
1
public/helvetiaLogo.svg
Normal file
1
public/helvetiaLogo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="169.939" height="39.846"><path fill="#181716" d="M6.015 9.5v10.053h.066c1.39-1.85 3.075-2.711 5.424-2.711 4.295 0 6.379 2.844 6.379 7.143v10.381h-6.016v-8.694c0-1.985-.298-4.032-2.712-4.032-2.379 0-3.141 2.047-3.141 4.032v8.694H0V9.5h6.015zm32.267 17.396H25.62c0 2.444 1.291 3.768 3.77 3.768 1.29 0 2.214-.431 2.876-1.553h5.785c-.96 4-4.761 5.818-8.629 5.818-5.62 0-9.819-3.173-9.819-9.025 0-5.655 3.867-9.061 9.389-9.061 5.885 0 9.289 3.638 9.289 9.424v.629zm-5.586-3.473c-.299-1.621-1.786-2.68-3.406-2.68-1.752 0-3.206.928-3.572 2.68h6.978zM46.05 34.366h-6.015V9.5h6.015v24.866zm11.415-8.263l4.034-8.698h6.745l-8.861 16.962h-3.835l-8.893-16.962h6.744l4.066 8.698zm27.972.793H72.775c0 2.444 1.289 3.768 3.77 3.768 1.289 0 2.215-.431 2.875-1.553h5.785c-.958 4-4.761 5.818-8.626 5.818-5.62 0-9.819-3.173-9.819-9.025 0-5.655 3.869-9.061 9.388-9.061 5.884 0 9.29 3.638 9.29 9.424v.629zm-5.588-3.473c-.296-1.621-1.784-2.68-3.403-2.68-1.753 0-3.208.928-3.571 2.68h6.974zM94.31 34.366h-6.016v-12h-1.95v-4.962h1.95v-5.09h6.016v5.09h3.405v4.962H94.31v12zm10.927-22.316a3.384 3.384 0 01-3.371 3.371 3.386 3.386 0 01-3.372-3.371 3.387 3.387 0 013.372-3.372 3.383 3.383 0 013.371 3.372zm-.362 22.316h-6.019V17.404h6.019v16.962zm21.522 0h-6.02V32.48h-.064c-1.06 1.688-3.141 2.448-5.124 2.448-5.026 0-8.563-4.199-8.563-9.06 0-4.862 3.472-9.027 8.498-9.027 1.948 0 3.997.728 5.254 2.216v-1.654h6.02v16.963zm-13.554-8.463c0 2.148 1.42 3.8 3.866 3.8s3.868-1.651 3.868-3.8c0-2.083-1.422-3.836-3.868-3.836s-3.866 1.753-3.866 3.836z"/><path fill="#563B6C" d="M147.832 27.238l-7.721 1.983 6-13.866 7.749-1.848z"/><path fill="#71518C" d="M145.726 1.699L153.506 0l3.132 7.181-7.759 1.786z"/><path fill="#8761A8" d="M134.379 28.239l11.347-26.54 3.153 7.268-8.768 20.254z"/><path fill="#118289" d="M140.111 29.221l7.721-1.983 12.938 2.16-7.701 2.04z"/><path fill="#46A8B3" d="M156.236 38.762l-24.472-4.396 2.615-6.127 18.69 3.199z"/><path fill="#9F1717" d="M148.879 8.967l7.759-1.786 13.301 30.508-7.662 2.157z"/><path fill="#C21924" d="M146.111 15.355l2.768-6.388 13.398 30.879-6.041-1.084z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
9
public/site.webmanifest
Normal file
9
public/site.webmanifest
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
1
public/sitemap.xml
Normal file
1
public/sitemap.xml
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://victoriaseguros.com/seguros/decesos</loc><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://victoriaseguros.com/seguros/salud</loc><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://victoriaseguros.com/seguros/mascotas</loc><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://victoriaseguros.com/seguros/vehiculos</loc><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://victoriaseguros.com/seguros/vida</loc><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://victoriaseguros.com/seguros/hogar</loc><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://victoriaseguros.com/formulario</loc><changefreq>daily</changefreq><priority>1.0</priority></url></urlset>
|
||||
129
src/TextAnimations/BlurText/BlurText.tsx
Normal file
129
src/TextAnimations/BlurText/BlurText.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
Installed from https://reactbits.dev/ts/tailwind/
|
||||
*/
|
||||
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
import { useSprings, animated, SpringValue } from "@react-spring/web";
|
||||
|
||||
const AnimatedSpan = animated.span as React.FC<
|
||||
React.HTMLAttributes<HTMLSpanElement>
|
||||
>;
|
||||
|
||||
interface BlurTextProps {
|
||||
text?: string;
|
||||
delay?: number;
|
||||
className?: string;
|
||||
animateBy?: "words" | "letters";
|
||||
direction?: "top" | "bottom";
|
||||
threshold?: number;
|
||||
rootMargin?: string;
|
||||
animationFrom?: Record<string, any>;
|
||||
animationTo?: Record<string, any>[];
|
||||
easing?: (t: number) => number | string;
|
||||
onAnimationComplete?: () => void;
|
||||
}
|
||||
|
||||
const BlurText: React.FC<BlurTextProps> = ({
|
||||
text = "",
|
||||
delay = 200,
|
||||
className = "",
|
||||
animateBy = "words",
|
||||
direction = "top",
|
||||
threshold = 0.1,
|
||||
rootMargin = "0px",
|
||||
animationFrom,
|
||||
animationTo,
|
||||
easing = "easeOutCubic",
|
||||
onAnimationComplete,
|
||||
}) => {
|
||||
const elements = animateBy === "words" ? text.split(" ") : text.split("");
|
||||
const [inView, setInView] = useState(false);
|
||||
const ref = useRef<HTMLParagraphElement>(null);
|
||||
const animatedCount = useRef(0);
|
||||
|
||||
// Default animations based on direction
|
||||
const defaultFrom: Record<string, any> =
|
||||
direction === "top"
|
||||
? {
|
||||
filter: "blur(10px)",
|
||||
opacity: 0,
|
||||
transform: "translate3d(0,-50px,0)",
|
||||
}
|
||||
: {
|
||||
filter: "blur(10px)",
|
||||
opacity: 0,
|
||||
transform: "translate3d(0,50px,0)",
|
||||
};
|
||||
|
||||
const defaultTo: Record<string, any>[] = [
|
||||
{
|
||||
filter: "blur(5px)",
|
||||
opacity: 0.5,
|
||||
transform:
|
||||
direction === "top" ? "translate3d(0,5px,0)" : "translate3d(0,-5px,0)",
|
||||
},
|
||||
{ filter: "blur(0px)", opacity: 1, transform: "translate3d(0,0,0)" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
setInView(true);
|
||||
if (ref.current) {
|
||||
observer.unobserve(ref.current);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ threshold, rootMargin },
|
||||
);
|
||||
|
||||
if (ref.current) {
|
||||
observer.observe(ref.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [threshold, rootMargin]);
|
||||
|
||||
const springs = useSprings(
|
||||
elements.length,
|
||||
elements.map((_, i) => ({
|
||||
from: animationFrom || defaultFrom,
|
||||
to: inView
|
||||
? async (
|
||||
next: (arg: Record<string, SpringValue<any>>) => Promise<void>,
|
||||
) => {
|
||||
for (const step of animationTo || defaultTo) {
|
||||
await next(step);
|
||||
}
|
||||
animatedCount.current += 1;
|
||||
if (
|
||||
animatedCount.current === elements.length &&
|
||||
onAnimationComplete
|
||||
) {
|
||||
onAnimationComplete();
|
||||
}
|
||||
}
|
||||
: animationFrom || defaultFrom,
|
||||
delay: i * delay,
|
||||
config: { easing: easing as any },
|
||||
})),
|
||||
);
|
||||
|
||||
return (
|
||||
<p ref={ref} className={`blur-text ${className} flex flex-wrap`}>
|
||||
{springs.map((props, index) => (
|
||||
<AnimatedSpan
|
||||
key={index}
|
||||
style={props}
|
||||
className="inline-block will-change-[transform,filter,opacity]"
|
||||
>
|
||||
{elements[index] === " " ? "\u00A0" : elements[index]}
|
||||
{animateBy === "words" && index < elements.length - 1 && "\u00A0"}
|
||||
</AnimatedSpan>
|
||||
))}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlurText;
|
||||
116
src/TextAnimations/CountUp/CountUp.tsx
Normal file
116
src/TextAnimations/CountUp/CountUp.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
Installed from https://reactbits.dev/ts/tailwind/
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useInView, useMotionValue, useSpring } from "framer-motion";
|
||||
|
||||
interface CountUpProps {
|
||||
to: number;
|
||||
from?: number;
|
||||
direction?: "up" | "down";
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
className?: string;
|
||||
startWhen?: boolean;
|
||||
separator?: string;
|
||||
onStart?: () => void;
|
||||
onEnd?: () => void;
|
||||
}
|
||||
|
||||
export default function CountUp({
|
||||
to,
|
||||
from = 0,
|
||||
direction = "up",
|
||||
delay = 0,
|
||||
duration = 2, // Duration of the animation in seconds
|
||||
className = "",
|
||||
startWhen = true,
|
||||
separator = "",
|
||||
onStart,
|
||||
onEnd,
|
||||
}: CountUpProps) {
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
const motionValue = useMotionValue(direction === "down" ? to : from);
|
||||
|
||||
// Calculate damping and stiffness based on duration
|
||||
const damping = 20 + 40 * (1 / duration); // Adjust this formula for finer control
|
||||
const stiffness = 100 * (1 / duration); // Adjust this formula for finer control
|
||||
|
||||
const springValue = useSpring(motionValue, {
|
||||
damping,
|
||||
stiffness,
|
||||
});
|
||||
|
||||
const isInView = useInView(ref, { once: true, margin: "0px" });
|
||||
|
||||
// Set initial text content to the initial value based on direction
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.textContent = String(direction === "down" ? to : from);
|
||||
}
|
||||
}, [from, to, direction]);
|
||||
|
||||
// Start the animation when in view and startWhen is true
|
||||
useEffect(() => {
|
||||
if (isInView && startWhen) {
|
||||
if (typeof onStart === "function") {
|
||||
onStart();
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
motionValue.set(direction === "down" ? from : to);
|
||||
}, delay * 1000);
|
||||
|
||||
const durationTimeoutId = setTimeout(
|
||||
() => {
|
||||
if (typeof onEnd === "function") {
|
||||
onEnd();
|
||||
}
|
||||
},
|
||||
delay * 1000 + duration * 1000,
|
||||
);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
clearTimeout(durationTimeoutId);
|
||||
};
|
||||
}
|
||||
}, [
|
||||
isInView,
|
||||
startWhen,
|
||||
motionValue,
|
||||
direction,
|
||||
from,
|
||||
to,
|
||||
delay,
|
||||
onStart,
|
||||
onEnd,
|
||||
duration,
|
||||
]);
|
||||
|
||||
// Update text content with formatted number on spring value change
|
||||
useEffect(() => {
|
||||
const unsubscribe = springValue.on("change", (latest) => {
|
||||
if (ref.current) {
|
||||
const options = {
|
||||
useGrouping: !!separator,
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
};
|
||||
|
||||
const formattedNumber = Intl.NumberFormat("en-US", options).format(
|
||||
Number(latest.toFixed(0)),
|
||||
);
|
||||
|
||||
ref.current.textContent = separator
|
||||
? formattedNumber.replace(/,/g, separator)
|
||||
: formattedNumber;
|
||||
}
|
||||
});
|
||||
|
||||
return () => unsubscribe();
|
||||
}, [springValue, separator]);
|
||||
|
||||
return <span className={`${className}`} ref={ref} />;
|
||||
}
|
||||
276
src/TextAnimations/RotatingText/RotatingText.tsx
Normal file
276
src/TextAnimations/RotatingText/RotatingText.tsx
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
Installed from https://reactbits.dev/ts/tailwind/
|
||||
*/
|
||||
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
motion,
|
||||
AnimatePresence,
|
||||
Transition,
|
||||
type VariantLabels,
|
||||
type Target,
|
||||
type AnimationControls,
|
||||
type TargetAndTransition,
|
||||
} from "framer-motion";
|
||||
|
||||
function cn(...classes: (string | undefined | null | boolean)[]): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export interface RotatingTextRef {
|
||||
next: () => void;
|
||||
previous: () => void;
|
||||
jumpTo: (index: number) => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export interface RotatingTextProps
|
||||
extends Omit<
|
||||
React.ComponentPropsWithoutRef<typeof motion.span>,
|
||||
"children" | "transition" | "initial" | "animate" | "exit"
|
||||
> {
|
||||
texts: string[];
|
||||
transition?: Transition;
|
||||
initial?: boolean | Target | VariantLabels;
|
||||
animate?: boolean | VariantLabels | AnimationControls | TargetAndTransition;
|
||||
exit?: Target | VariantLabels;
|
||||
animatePresenceMode?: "sync" | "wait";
|
||||
animatePresenceInitial?: boolean;
|
||||
rotationInterval?: number;
|
||||
staggerDuration?: number;
|
||||
staggerFrom?: "first" | "last" | "center" | "random" | number;
|
||||
loop?: boolean;
|
||||
auto?: boolean;
|
||||
splitBy?: string;
|
||||
onNext?: (index: number) => void;
|
||||
mainClassName?: string;
|
||||
splitLevelClassName?: string;
|
||||
elementLevelClassName?: string;
|
||||
}
|
||||
|
||||
const RotatingText = forwardRef<RotatingTextRef, RotatingTextProps>(
|
||||
(
|
||||
{
|
||||
texts,
|
||||
transition = { type: "spring", damping: 25, stiffness: 300 },
|
||||
initial = { y: "100%", opacity: 0 },
|
||||
animate = { y: 0, opacity: 1 },
|
||||
exit = { y: "-120%", opacity: 0 },
|
||||
animatePresenceMode = "wait",
|
||||
animatePresenceInitial = false,
|
||||
rotationInterval = 2000,
|
||||
staggerDuration = 0,
|
||||
staggerFrom = "first",
|
||||
loop = true,
|
||||
auto = true,
|
||||
splitBy = "characters",
|
||||
onNext,
|
||||
mainClassName,
|
||||
splitLevelClassName,
|
||||
elementLevelClassName,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [currentTextIndex, setCurrentTextIndex] = useState<number>(0);
|
||||
|
||||
const splitIntoCharacters = (text: string): string[] => {
|
||||
if (typeof Intl !== "undefined" && Intl.Segmenter) {
|
||||
const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
|
||||
return Array.from(
|
||||
segmenter.segment(text),
|
||||
(segment) => segment.segment,
|
||||
);
|
||||
}
|
||||
return Array.from(text);
|
||||
};
|
||||
|
||||
const elements = useMemo(() => {
|
||||
const currentText: string = texts[currentTextIndex];
|
||||
if (splitBy === "characters") {
|
||||
const words = currentText.split(" ");
|
||||
return words.map((word, i) => ({
|
||||
characters: splitIntoCharacters(word),
|
||||
needsSpace: i !== words.length - 1,
|
||||
}));
|
||||
}
|
||||
if (splitBy === "words") {
|
||||
return currentText.split(" ").map((word, i, arr) => ({
|
||||
characters: [word],
|
||||
needsSpace: i !== arr.length - 1,
|
||||
}));
|
||||
}
|
||||
if (splitBy === "lines") {
|
||||
return currentText.split("\n").map((line, i, arr) => ({
|
||||
characters: [line],
|
||||
needsSpace: i !== arr.length - 1,
|
||||
}));
|
||||
}
|
||||
|
||||
return currentText.split(splitBy).map((part, i, arr) => ({
|
||||
characters: [part],
|
||||
needsSpace: i !== arr.length - 1,
|
||||
}));
|
||||
}, [texts, currentTextIndex, splitBy]);
|
||||
|
||||
const getStaggerDelay = useCallback(
|
||||
(index: number, totalChars: number): number => {
|
||||
const total = totalChars;
|
||||
if (staggerFrom === "first") return index * staggerDuration;
|
||||
if (staggerFrom === "last")
|
||||
return (total - 1 - index) * staggerDuration;
|
||||
if (staggerFrom === "center") {
|
||||
const center = Math.floor(total / 2);
|
||||
return Math.abs(center - index) * staggerDuration;
|
||||
}
|
||||
if (staggerFrom === "random") {
|
||||
const randomIndex = Math.floor(Math.random() * total);
|
||||
return Math.abs(randomIndex - index) * staggerDuration;
|
||||
}
|
||||
return Math.abs((staggerFrom as number) - index) * staggerDuration;
|
||||
},
|
||||
[staggerFrom, staggerDuration],
|
||||
);
|
||||
|
||||
const handleIndexChange = useCallback(
|
||||
(newIndex: number) => {
|
||||
setCurrentTextIndex(newIndex);
|
||||
if (onNext) onNext(newIndex);
|
||||
},
|
||||
[onNext],
|
||||
);
|
||||
|
||||
const next = useCallback(() => {
|
||||
const nextIndex =
|
||||
currentTextIndex === texts.length - 1
|
||||
? loop
|
||||
? 0
|
||||
: currentTextIndex
|
||||
: currentTextIndex + 1;
|
||||
if (nextIndex !== currentTextIndex) {
|
||||
handleIndexChange(nextIndex);
|
||||
}
|
||||
}, [currentTextIndex, texts.length, loop, handleIndexChange]);
|
||||
|
||||
const previous = useCallback(() => {
|
||||
const prevIndex =
|
||||
currentTextIndex === 0
|
||||
? loop
|
||||
? texts.length - 1
|
||||
: currentTextIndex
|
||||
: currentTextIndex - 1;
|
||||
if (prevIndex !== currentTextIndex) {
|
||||
handleIndexChange(prevIndex);
|
||||
}
|
||||
}, [currentTextIndex, texts.length, loop, handleIndexChange]);
|
||||
|
||||
const jumpTo = useCallback(
|
||||
(index: number) => {
|
||||
const validIndex = Math.max(0, Math.min(index, texts.length - 1));
|
||||
if (validIndex !== currentTextIndex) {
|
||||
handleIndexChange(validIndex);
|
||||
}
|
||||
},
|
||||
[texts.length, currentTextIndex, handleIndexChange],
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
if (currentTextIndex !== 0) {
|
||||
handleIndexChange(0);
|
||||
}
|
||||
}, [currentTextIndex, handleIndexChange]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
next,
|
||||
previous,
|
||||
jumpTo,
|
||||
reset,
|
||||
}),
|
||||
[next, previous, jumpTo, reset],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!auto) return;
|
||||
const intervalId = setInterval(next, rotationInterval);
|
||||
return () => clearInterval(intervalId);
|
||||
}, [next, rotationInterval, auto]);
|
||||
|
||||
return (
|
||||
<motion.span
|
||||
className={cn(
|
||||
"flex flex-wrap whitespace-pre-wrap relative",
|
||||
mainClassName,
|
||||
)}
|
||||
{...rest}
|
||||
layout
|
||||
transition={transition}
|
||||
>
|
||||
<span className="sr-only">{texts[currentTextIndex]}</span>
|
||||
<AnimatePresence
|
||||
mode={animatePresenceMode}
|
||||
initial={animatePresenceInitial}
|
||||
>
|
||||
<motion.div
|
||||
key={currentTextIndex}
|
||||
className={cn(
|
||||
splitBy === "lines"
|
||||
? "flex flex-col w-full"
|
||||
: "flex flex-wrap whitespace-pre-wrap relative",
|
||||
)}
|
||||
layout
|
||||
aria-hidden="true"
|
||||
>
|
||||
{elements.map((wordObj, wordIndex, array) => {
|
||||
const previousCharsCount = array
|
||||
.slice(0, wordIndex)
|
||||
.reduce((sum, word) => sum + word.characters.length, 0);
|
||||
return (
|
||||
<span
|
||||
key={wordIndex}
|
||||
className={cn("inline-flex", splitLevelClassName)}
|
||||
>
|
||||
{wordObj.characters.map((char, charIndex) => (
|
||||
<motion.span
|
||||
key={charIndex}
|
||||
initial={initial}
|
||||
animate={animate}
|
||||
exit={exit}
|
||||
transition={{
|
||||
...transition,
|
||||
delay: getStaggerDelay(
|
||||
previousCharsCount + charIndex,
|
||||
array.reduce(
|
||||
(sum, word) => sum + word.characters.length,
|
||||
0,
|
||||
),
|
||||
),
|
||||
}}
|
||||
className={cn("inline-block", elementLevelClassName)}
|
||||
>
|
||||
{char}
|
||||
</motion.span>
|
||||
))}
|
||||
{wordObj.needsSpace && (
|
||||
<span className="whitespace-pre"> </span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</motion.span>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
RotatingText.displayName = "RotatingText";
|
||||
export default RotatingText;
|
||||
174
src/TextAnimations/TextCursor/TextCursor.tsx
Normal file
174
src/TextAnimations/TextCursor/TextCursor.tsx
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
Installed from https://reactbits.dev/ts/tailwind/
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
interface TextCursorProps {
|
||||
text: string;
|
||||
delay?: number;
|
||||
spacing?: number;
|
||||
followMouseDirection?: boolean;
|
||||
randomFloat?: boolean;
|
||||
exitDuration?: number;
|
||||
removalInterval?: number;
|
||||
maxPoints?: number;
|
||||
}
|
||||
|
||||
interface TrailItem {
|
||||
id: number;
|
||||
x: number;
|
||||
y: number;
|
||||
angle: number;
|
||||
randomX?: number;
|
||||
randomY?: number;
|
||||
randomRotate?: number;
|
||||
}
|
||||
|
||||
const TextCursor: React.FC<TextCursorProps> = ({
|
||||
text = "⚛️",
|
||||
delay = 0.01,
|
||||
spacing = 100,
|
||||
followMouseDirection = true,
|
||||
randomFloat = true,
|
||||
exitDuration = 0.5,
|
||||
removalInterval = 30,
|
||||
maxPoints = 5,
|
||||
}) => {
|
||||
const [trail, setTrail] = useState<TrailItem[]>([]);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const lastMoveTimeRef = useRef<number>(Date.now());
|
||||
const idCounter = useRef<number>(0);
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const mouseX = e.clientX - rect.left;
|
||||
const mouseY = e.clientY - rect.top;
|
||||
|
||||
setTrail((prev) => {
|
||||
let newTrail = [...prev];
|
||||
if (newTrail.length === 0) {
|
||||
newTrail.push({
|
||||
id: idCounter.current++,
|
||||
x: mouseX,
|
||||
y: mouseY,
|
||||
angle: 0,
|
||||
...(randomFloat && {
|
||||
randomX: Math.random() * 10 - 5,
|
||||
randomY: Math.random() * 10 - 5,
|
||||
randomRotate: Math.random() * 10 - 5,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
const last = newTrail[newTrail.length - 1];
|
||||
const dx = mouseX - last.x;
|
||||
const dy = mouseY - last.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
if (distance >= spacing) {
|
||||
let rawAngle = (Math.atan2(dy, dx) * 180) / Math.PI;
|
||||
if (rawAngle > 90) rawAngle -= 180;
|
||||
else if (rawAngle < -90) rawAngle += 180;
|
||||
const computedAngle = followMouseDirection ? rawAngle : 0;
|
||||
const steps = Math.floor(distance / spacing);
|
||||
for (let i = 1; i <= steps; i++) {
|
||||
const t = (spacing * i) / distance;
|
||||
const newX = last.x + dx * t;
|
||||
const newY = last.y + dy * t;
|
||||
newTrail.push({
|
||||
id: idCounter.current++,
|
||||
x: newX,
|
||||
y: newY,
|
||||
angle: computedAngle,
|
||||
...(randomFloat && {
|
||||
randomX: Math.random() * 10 - 5,
|
||||
randomY: Math.random() * 10 - 5,
|
||||
randomRotate: Math.random() * 10 - 5,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newTrail.length > maxPoints) {
|
||||
newTrail = newTrail.slice(newTrail.length - maxPoints);
|
||||
}
|
||||
return newTrail;
|
||||
});
|
||||
lastMoveTimeRef.current = Date.now();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
container.addEventListener("mousemove", handleMouseMove);
|
||||
return () => container.removeEventListener("mousemove", handleMouseMove);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (Date.now() - lastMoveTimeRef.current > 100) {
|
||||
setTrail((prev) => (prev.length > 0 ? prev.slice(1) : prev));
|
||||
}
|
||||
}, removalInterval);
|
||||
return () => clearInterval(interval);
|
||||
}, [removalInterval]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="w-full h-full relative">
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<AnimatePresence>
|
||||
{trail.map((item) => (
|
||||
<motion.div
|
||||
key={item.id}
|
||||
initial={{ opacity: 0, scale: 1, x: 0, y: 0, rotate: item.angle }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
x: randomFloat ? [0, item.randomX || 0, 0] : 0,
|
||||
y: randomFloat ? [0, item.randomY || 0, 0] : 0,
|
||||
rotate: randomFloat
|
||||
? [
|
||||
item.angle,
|
||||
item.angle + (item.randomRotate || 0),
|
||||
item.angle,
|
||||
]
|
||||
: item.angle,
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0 }}
|
||||
transition={{
|
||||
opacity: { duration: exitDuration, ease: "easeOut", delay },
|
||||
...(randomFloat && {
|
||||
x: {
|
||||
duration: 2,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
},
|
||||
y: {
|
||||
duration: 2,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
},
|
||||
rotate: {
|
||||
duration: 2,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
},
|
||||
}),
|
||||
}}
|
||||
className="absolute select-none whitespace-nowrap text-3xl"
|
||||
style={{ left: item.x, top: item.y }}
|
||||
>
|
||||
{text}
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextCursor;
|
||||
6
src/api.ts
Normal file
6
src/api.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {
|
||||
createStartAPIHandler,
|
||||
defaultAPIFileRouteHandler,
|
||||
} from '@tanstack/react-start/api'
|
||||
|
||||
export default createStartAPIHandler(defaultAPIFileRouteHandler)
|
||||
8
src/client.tsx
Normal file
8
src/client.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
/// <reference types="vinxi/types/client" />
|
||||
import { hydrateRoot } from 'react-dom/client'
|
||||
import { StartClient } from '@tanstack/react-start'
|
||||
import { createRouter } from './router'
|
||||
|
||||
const router = createRouter()
|
||||
|
||||
hydrateRoot(document, <StartClient router={router} />)
|
||||
50
src/components/ButtonCall.tsx
Normal file
50
src/components/ButtonCall.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { Button } from "@heroui/button";
|
||||
import { getRouteApi, Link } from "@tanstack/react-router";
|
||||
|
||||
const routeApi = getRouteApi("/");
|
||||
|
||||
function ButtonCall({
|
||||
text = "Solicitar llamada",
|
||||
secure,
|
||||
}: {
|
||||
text?: string;
|
||||
secure: string;
|
||||
}) {
|
||||
const navigate = routeApi.useNavigate();
|
||||
const waUrl = `https://wa.me/${+34633620767}`;
|
||||
return (
|
||||
<div className="flex items-center gap-2 justify-center p-2 mt-2">
|
||||
<Button
|
||||
endContent={
|
||||
<span className="iconify cib--whatsapp size-5 text-green-700" />
|
||||
}
|
||||
color="success"
|
||||
variant="flat"
|
||||
as={Link}
|
||||
to={waUrl}
|
||||
isIconOnly
|
||||
size="lg"
|
||||
/>
|
||||
<Button
|
||||
size="lg"
|
||||
endContent={
|
||||
<span className="iconify size-4 solar--alt-arrow-right-outline" />
|
||||
}
|
||||
color="secondary"
|
||||
onPress={() => {
|
||||
navigate({
|
||||
to: "/formulario",
|
||||
viewTransition: true,
|
||||
search: {
|
||||
seguro: `${secure || ""}`,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ButtonCall;
|
||||
58
src/components/Cookies.tsx
Normal file
58
src/components/Cookies.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { Button } from "@heroui/button";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCookies } from "react-cookie";
|
||||
|
||||
export default function CookieConsent() {
|
||||
const [cookies, setCookie, removeCookie] = useCookies(["cookieConsent"]);
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!cookies.cookieConsent) {
|
||||
setVisible(true);
|
||||
}
|
||||
}, [cookies]);
|
||||
|
||||
const acceptCookies = () => {
|
||||
setCookie("cookieConsent", "true", {
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 365,
|
||||
});
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
// const rejectCookies = () => {
|
||||
// removeCookie("cookieConsent", { path: "/" });
|
||||
// setVisible(false);
|
||||
// };
|
||||
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 left-4 right-4 md:left-auto md:right-4 max-w-md mx-auto z-50 bg-white shadow-lg rounded-lg p-4 border border-gray-200">
|
||||
<p className="text-sm text-gray-700">
|
||||
Utilizamos cookies para mejorar tu experiencia en el sitio. Al continuar
|
||||
navegando, aceptas nuestra{" "}
|
||||
<a href="/formulario/politicas" className="text-blue-600 underline">
|
||||
política de cookies
|
||||
</a>
|
||||
<a className="px-2">/</a>
|
||||
|
||||
<a href="/formulario/politicas-formulario" className="text-blue-600 underline">
|
||||
política de privacidad
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<div className="flex gap-4 mt-4">
|
||||
<Button onPress={acceptCookies} color="primary">Aceptar</Button>
|
||||
{/* <button
|
||||
onClick={rejectCookies}
|
||||
className="px-4 py-2 bg-gray-400 text-white text-sm font-medium rounded hover:bg-gray-500 transition"
|
||||
>
|
||||
Rechazar
|
||||
</button> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
53
src/components/DefaultCatchBoundary.tsx
Normal file
53
src/components/DefaultCatchBoundary.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import {
|
||||
ErrorComponent,
|
||||
Link,
|
||||
rootRouteId,
|
||||
useMatch,
|
||||
useRouter,
|
||||
} from '@tanstack/react-router'
|
||||
import type { ErrorComponentProps } from '@tanstack/react-router'
|
||||
|
||||
export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
||||
const router = useRouter()
|
||||
const isRoot = useMatch({
|
||||
strict: false,
|
||||
select: (state) => state.id === rootRouteId,
|
||||
})
|
||||
|
||||
console.error('DefaultCatchBoundary Error:', error)
|
||||
|
||||
return (
|
||||
<div className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6">
|
||||
<ErrorComponent error={error} />
|
||||
<div className="flex gap-2 items-center flex-wrap">
|
||||
<button
|
||||
onClick={() => {
|
||||
router.invalidate()
|
||||
}}
|
||||
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
|
||||
>
|
||||
Try Again
|
||||
</button>
|
||||
{isRoot ? (
|
||||
<Link
|
||||
to="/"
|
||||
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to="/"
|
||||
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
window.history.back()
|
||||
}}
|
||||
>
|
||||
Go Back
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
76
src/components/Footer.tsx
Normal file
76
src/components/Footer.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { Image } from "@heroui/image";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
export const Footer = () => {
|
||||
return (
|
||||
<footer className="body-font">
|
||||
<div className="container px-5 py-4 mx-auto flex items-center sm:flex-row flex-col">
|
||||
<div className="flex items-center flex-wrap sm:flex-row flex-col gap-2">
|
||||
<div className="flex title-font font-medium items-center md:justify-start justify-center">
|
||||
<Image
|
||||
src="/victoria segurosLogo.svg"
|
||||
alt="Logo Seguros"
|
||||
width={100}
|
||||
height={30}
|
||||
className="px-2 p-1"
|
||||
fallbackSrc="/victoria segurosLogo.svg"
|
||||
/>
|
||||
<span className="ml-3 text-sm">Victoria Seguros</span>
|
||||
</div>
|
||||
<p className="hidden sm:flex">
|
||||
<span>{` - `}</span>
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 sm:border-gray-200 sm:py-2 sm:mt-0 mt-4">
|
||||
© Copyright {new Date().getFullYear()}
|
||||
</p>
|
||||
<p>
|
||||
<Link
|
||||
to="/formulario/politicas"
|
||||
className="text-sm text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
Política de Cookies
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span className="inline-flex sm:ml-auto sm:mt-0 mt-4 justify-center sm:justify-start gap-2">
|
||||
<Chip
|
||||
startContent={
|
||||
<span className="iconify size-5 text-default-500 solar--phone-calling-rounded-bold-duotone" />
|
||||
}
|
||||
classNames={{
|
||||
content: "mx-1 items-center",
|
||||
}}
|
||||
>
|
||||
<a href="tel:+34633620767">633620767</a>
|
||||
</Chip>
|
||||
<a href="https://www.facebook.com/-/?locale=es_ES">
|
||||
<Chip
|
||||
className="px-2"
|
||||
variant="light"
|
||||
startContent={
|
||||
<span className="iconify cib--facebook-f size-4 text-default-500" />
|
||||
}
|
||||
classNames={{
|
||||
content: "p-0 m-0",
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
<a href="https://es.linkedin.com/company/">
|
||||
<Chip
|
||||
className="px-2"
|
||||
variant="light"
|
||||
startContent={
|
||||
<span className="iconify cib--linkedin-in size-4 text-default-500" />
|
||||
}
|
||||
classNames={{
|
||||
content: "p-0 m-0",
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
105
src/components/Kpi.tsx
Normal file
105
src/components/Kpi.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import { Card } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { cn } from "@heroui/theme";
|
||||
|
||||
type TrendCardProps = {
|
||||
title: string;
|
||||
value: string;
|
||||
change: string;
|
||||
changeType: "positive" | "neutral" | "negative";
|
||||
trendType: "up" | "neutral" | "down";
|
||||
trendChipPosition?: "top" | "bottom";
|
||||
trendChipVariant?: "flat" | "light";
|
||||
};
|
||||
|
||||
const data: TrendCardProps[] = [
|
||||
{
|
||||
title: "Hogar",
|
||||
value: "+300",
|
||||
change: "0.0%",
|
||||
changeType: "neutral",
|
||||
trendType: "neutral",
|
||||
},
|
||||
{
|
||||
title: "Decesos",
|
||||
value: "+1500",
|
||||
change: "1.0%",
|
||||
changeType: "positive",
|
||||
trendType: "up",
|
||||
},
|
||||
{
|
||||
title: "Vehículos",
|
||||
value: "+200",
|
||||
change: "1.0%",
|
||||
changeType: "positive",
|
||||
trendType: "up",
|
||||
},
|
||||
{
|
||||
title: "Mascotas",
|
||||
value: "+100",
|
||||
change: "1.0%",
|
||||
changeType: "positive",
|
||||
trendType: "up",
|
||||
},
|
||||
];
|
||||
|
||||
const TrendCard = ({
|
||||
title,
|
||||
value,
|
||||
change,
|
||||
changeType,
|
||||
trendType,
|
||||
trendChipPosition = "top",
|
||||
trendChipVariant = "light",
|
||||
}: TrendCardProps) => {
|
||||
return (
|
||||
<Card className=" border border-transparent dark:border-default-100">
|
||||
<div className="flex p-4">
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<dt className="text-small font-medium text-default-500">{title}</dt>
|
||||
<dd className="text-2xl font-semibold text-default-700">{value}</dd>
|
||||
</div>
|
||||
<Chip
|
||||
className={cn("absolute right-4", {
|
||||
"top-4": trendChipPosition === "top",
|
||||
"bottom-4": trendChipPosition === "bottom",
|
||||
})}
|
||||
classNames={{
|
||||
content: "font-medium text-[0.65rem]",
|
||||
}}
|
||||
color={
|
||||
changeType === "positive"
|
||||
? "success"
|
||||
: changeType === "neutral"
|
||||
? "warning"
|
||||
: "danger"
|
||||
}
|
||||
radius="sm"
|
||||
size="sm"
|
||||
startContent={
|
||||
trendType === "up" ? (
|
||||
<span className="size-12 iconify solar--arrow-right-up-linear" />
|
||||
) : trendType === "neutral" ? (
|
||||
<span className="size-12 iconify solar--arrow-right-linear" />
|
||||
) : (
|
||||
<span className="size-12 iconify solar--arrow-right-down-linear" />
|
||||
)
|
||||
}
|
||||
variant={trendChipVariant}
|
||||
>
|
||||
{change}
|
||||
</Chip>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Kpi() {
|
||||
return (
|
||||
<dl className="grid w-full grid-cols-1 gap-5 sm:grid-cols-2 md:grid-cols-3">
|
||||
{data.map((props, index) => (
|
||||
<TrendCard key={index} {...props} />
|
||||
))}
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
124
src/components/Modal.tsx
Normal file
124
src/components/Modal.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
import {
|
||||
Modal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
useDisclosure,
|
||||
} from "@heroui/modal";
|
||||
|
||||
import { Form } from "@heroui/form";
|
||||
import { Input, Textarea } from "@heroui/input";
|
||||
import { Select, SelectItem } from "@heroui/select";
|
||||
|
||||
interface PropsForm {
|
||||
// TODO: Replace zod infer
|
||||
name: string;
|
||||
time: string;
|
||||
description: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
import { Button } from "@heroui/button";
|
||||
import { toast } from "sonner";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export default function ModalComponent() {
|
||||
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
||||
|
||||
const { mutate } = useMutation({
|
||||
mutationKey: ["send-email"],
|
||||
mutationFn: async (data: PropsForm) => {
|
||||
const { name, time, description, email } = data;
|
||||
await axios.post(
|
||||
"https://sender-nr0t.onrender.com/sender",
|
||||
{
|
||||
to: "p3n4lv3r@gmail.com",
|
||||
subject: "Correo decesos",
|
||||
message: `
|
||||
<p>Nombre: ${name} </p>
|
||||
<p>Horario: ${time} </p>
|
||||
<br />
|
||||
<p>Descripción: ${description} </p>
|
||||
<p>Email: ${email} </p>
|
||||
`,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "insomnia/10.3.1",
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success("Petición enviada", { id: "email-toast" });
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("Error al enviar petición", { id: "email-toast" });
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(event.currentTarget);
|
||||
|
||||
const name = formData.get("nombre") as string;
|
||||
const time = formData.get("horario") as string;
|
||||
const description = formData.get("descripcion") as string;
|
||||
const email = formData.get("email") as string;
|
||||
|
||||
toast.loading("Enviando", { id: "email-toast" });
|
||||
|
||||
mutate({ name, time, description, email });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <Button onPress={onOpen}>Open Modal</Button> */}
|
||||
<Button color="primary" onPress={onOpen}>
|
||||
Solicitar oferta
|
||||
</Button>
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange} isDismissable={false}>
|
||||
<ModalContent>
|
||||
{() => (
|
||||
<>
|
||||
<Form validationBehavior="native" onSubmit={onSubmit}>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
Formulario contacto
|
||||
</ModalHeader>
|
||||
<ModalBody className="w-full">
|
||||
<div className="grid gap-2">
|
||||
<Input name="nombre" label="Nombre" isRequired />
|
||||
<Input name="email" label="Email" isRequired />
|
||||
<Textarea
|
||||
name="descripcion"
|
||||
label="Descripción"
|
||||
isRequired
|
||||
/>
|
||||
<Select
|
||||
name="horario"
|
||||
label="Horario"
|
||||
defaultSelectedKeys={["mor"]}
|
||||
>
|
||||
<SelectItem key="mor" value="9:30 - 14:00">
|
||||
Mañana 9:30 - 14:00
|
||||
</SelectItem>
|
||||
<SelectItem key="aft" value="16:00 - 20:00">
|
||||
Tarde 16:00 - 20:00
|
||||
</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter className=" flex w-full justify-end">
|
||||
<Button type="submit">Enviar</Button>
|
||||
</ModalFooter>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
230
src/components/NavBarComponents.tsx
Normal file
230
src/components/NavBarComponents.tsx
Normal file
@ -0,0 +1,230 @@
|
||||
import type { NavbarProps } from "@heroui/navbar";
|
||||
import {
|
||||
Navbar,
|
||||
NavbarBrand,
|
||||
NavbarContent,
|
||||
NavbarItem,
|
||||
NavbarMenu,
|
||||
NavbarMenuItem,
|
||||
NavbarMenuToggle,
|
||||
} from "@heroui/navbar";
|
||||
|
||||
import React from "react";
|
||||
// import { Link } from "@heroui/link";
|
||||
import {
|
||||
Link,
|
||||
useMatch,
|
||||
useMatchRoute,
|
||||
useNavigate,
|
||||
} from "@tanstack/react-router";
|
||||
import { Button } from "@heroui/button";
|
||||
import { cn } from "@heroui/theme";
|
||||
import { Divider } from "@heroui/divider";
|
||||
import ButtonCall from "./ButtonCall";
|
||||
import { Chip } from "@heroui/chip";
|
||||
|
||||
// import {Icon} from "@iconify/react";
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
icon: "",
|
||||
text: "Decesos",
|
||||
link: "/seguros/decesos/",
|
||||
},
|
||||
{
|
||||
icon: "",
|
||||
text: "Salud",
|
||||
link: "/seguros/salud/",
|
||||
},
|
||||
{
|
||||
icon: "",
|
||||
text: "Hogar",
|
||||
link: "/seguros/hogar/",
|
||||
},
|
||||
{
|
||||
icon: "",
|
||||
text: "Mascotas",
|
||||
link: "/seguros/mascotas/",
|
||||
},
|
||||
{
|
||||
icon: "",
|
||||
text: "Vehículos",
|
||||
link: "/seguros/vehiculos/",
|
||||
},
|
||||
{
|
||||
icon: "",
|
||||
text: "Vida",
|
||||
link: "/seguros/vida/",
|
||||
},
|
||||
];
|
||||
|
||||
const activeLinkProps = {
|
||||
style: { fontWeight: "bold" },
|
||||
};
|
||||
|
||||
const navLinks = [
|
||||
{ to: "/", label: "Inicio", className: "text-lg" },
|
||||
{ to: "/seguros/decesos", label: "Decesos" },
|
||||
{ to: "/seguros/hogar", label: "Hogar" },
|
||||
{ to: "/seguros/salud", label: "Salud" },
|
||||
{ to: "/seguros/mascotas", label: "Mascotas" },
|
||||
{ to: "/seguros/vida", label: "Vida" },
|
||||
{
|
||||
to: "/seguros/vehiculos",
|
||||
label: "Vehículos",
|
||||
className: "data-[active='true']:font-medium",
|
||||
},
|
||||
];
|
||||
|
||||
const BasicNavbar = React.forwardRef<HTMLElement, NavbarProps>(
|
||||
({ classNames = {}, ...props }, ref) => {
|
||||
const navigate = useNavigate();
|
||||
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
|
||||
const delayedSetIsMenuOpen = (val: boolean) => {
|
||||
setTimeout(() => {
|
||||
setIsMenuOpen(val);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
return (
|
||||
<Navbar
|
||||
ref={ref}
|
||||
{...props}
|
||||
classNames={{
|
||||
base: cn("border-default-100 bg-transparent", {
|
||||
"bg-white/50": isMenuOpen,
|
||||
}),
|
||||
wrapper: "w-full justify-center",
|
||||
item: "hidden md:flex",
|
||||
...classNames,
|
||||
}}
|
||||
height="60px"
|
||||
isMenuOpen={isMenuOpen}
|
||||
onMenuOpenChange={setIsMenuOpen}
|
||||
>
|
||||
{/* Left Content */}
|
||||
<NavbarBrand>
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/",
|
||||
viewTransition: true,
|
||||
})
|
||||
}
|
||||
className="mx-0 px-0"
|
||||
variant="light"
|
||||
>
|
||||
<span className="text-lg font-medium text-purple-900">
|
||||
Victoria<span className="text-gray-500">Seguros</span>
|
||||
</span>
|
||||
</Button>
|
||||
</NavbarBrand>
|
||||
|
||||
{/* Center Content */}
|
||||
<NavbarContent>
|
||||
{navLinks.map(({ to, label, className }) => (
|
||||
<Link key={to} to={to} viewTransition activeProps={activeLinkProps}>
|
||||
{() => (
|
||||
<NavbarItem className={` text-lg ${className}`}>
|
||||
{label}
|
||||
</NavbarItem>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
<NavbarItem>
|
||||
<Chip
|
||||
color="success"
|
||||
variant="flat"
|
||||
className=""
|
||||
startContent={
|
||||
<span className="iconify size-5 text-default-500 solar--phone-calling-rounded-bold-duotone" />
|
||||
}
|
||||
classNames={{
|
||||
content: "mx-1 items-center font-semibold",
|
||||
}}
|
||||
>
|
||||
<a href="tel:+34633620767">633620767</a>
|
||||
</Chip>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
<NavbarContent />
|
||||
<NavbarMenuToggle className="text-default-400 md:hidden" />
|
||||
<NavbarMenu
|
||||
// className="top-[calc(var(--navbar-height)_-_1px)] max-h-fit bg-default-200/50 shadow-medium backdrop-saturate-150 dark:bg-default-100/50"
|
||||
motionProps={{
|
||||
initial: { opacity: 0, y: -20 },
|
||||
animate: { opacity: 1, y: 0 },
|
||||
exit: { opacity: 0, y: -20 },
|
||||
transition: {
|
||||
ease: "easeInOut",
|
||||
duration: 0.2,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NavbarMenuItem className="">
|
||||
<Button
|
||||
className="mt-6"
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onPress={() => {
|
||||
navigate({
|
||||
to: "/formulario",
|
||||
viewTransition: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Calcular precio
|
||||
</Button>
|
||||
</NavbarMenuItem>
|
||||
<NavbarMenuItem>
|
||||
<div className="justify-center flex">
|
||||
<Chip
|
||||
color="success"
|
||||
variant="flat"
|
||||
startContent={
|
||||
<span className="iconify size-5 text-default-500 solar--phone-calling-rounded-bold-duotone" />
|
||||
}
|
||||
classNames={{
|
||||
content: "mx-1 items-center font-semibold",
|
||||
}}
|
||||
>
|
||||
<a href="tel:+34633620767" className="z-50">
|
||||
633620767
|
||||
</a>
|
||||
</Chip>
|
||||
</div>
|
||||
</NavbarMenuItem>
|
||||
|
||||
{menuItems.map((mi) => (
|
||||
<NavbarMenuItem
|
||||
key={mi.text}
|
||||
className="my-5 text-start font-black font-20 text-default-700 uppercase rounded-lg px-2"
|
||||
>
|
||||
<div className="flex gap-2 items-center ">
|
||||
<Link
|
||||
to={mi.link}
|
||||
viewTransition
|
||||
className="w-full h-full"
|
||||
onClick={() => delayedSetIsMenuOpen(false)}
|
||||
activeProps={{
|
||||
style: {
|
||||
color: "#000",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{mi.text}
|
||||
</Link>
|
||||
<span className="iconify size-6 solar--alt-arrow-right-line-duotone text-secondary/80" />
|
||||
</div>
|
||||
</NavbarMenuItem>
|
||||
))}
|
||||
</NavbarMenu>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
BasicNavbar.displayName = "BasicNavbar";
|
||||
|
||||
export default BasicNavbar;
|
||||
34
src/components/NotFound.tsx
Normal file
34
src/components/NotFound.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Card, CardBody } from "@heroui/card";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
export function NotFound({ children }: { children?: any }) {
|
||||
return (
|
||||
<div className="flex justify-center w-full">
|
||||
<Card className="w-full max-w-5xl min-h-[200px]">
|
||||
<CardBody className="p-2 flex justify-center flex-col items-center gap-5">
|
||||
<div className="text-gray-600 dark:text-gray-400 my-2 flex gap-2 items-center font-semibold">
|
||||
<span className="iconify size-6 solar--magnifer-zoom-out-bold-duotone " />
|
||||
{children || <p>La página que buscas no existe.</p>}
|
||||
</div>
|
||||
<div>
|
||||
<p className="flex items-center gap-2 flex-wrap">
|
||||
<button
|
||||
onClick={() => window.history.back()}
|
||||
className="bg-emerald-500 text-white px-2 py-1 rounded uppercase font-black text-sm"
|
||||
>
|
||||
Volver
|
||||
</button>
|
||||
<Link
|
||||
to="/"
|
||||
className="bg-cyan-600 text-white px-2 py-1 rounded uppercase font-black text-sm"
|
||||
>
|
||||
Ir a inicio
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
5
src/components/PostError.tsx
Normal file
5
src/components/PostError.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router'
|
||||
|
||||
export function PostErrorComponent({ error }: ErrorComponentProps) {
|
||||
return <ErrorComponent error={error} />
|
||||
}
|
||||
31
src/components/ThemeSwitcher.tsx
Normal file
31
src/components/ThemeSwitcher.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {useTheme} from "@heroui/use-theme";
|
||||
import { Button } from "@heroui/button";
|
||||
import { cn } from "@heroui/theme";
|
||||
|
||||
export function ThemeSwitcher() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const [isSelected, setIsSelected] = useState<boolean>(theme === "dark");
|
||||
|
||||
useEffect(() => {
|
||||
const newTheme = isSelected ? "dark" : "light";
|
||||
setTheme(newTheme);
|
||||
}, [isSelected, setTheme]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
onPress={() => setIsSelected(!isSelected)}
|
||||
isIconOnly
|
||||
variant="flat"
|
||||
radius="full"
|
||||
startContent={
|
||||
<span
|
||||
className={cn("size-5 iconify solar--moon-stars-line-duotone", {
|
||||
"solar--sun-fog-line-duotone": isSelected,
|
||||
})}
|
||||
></span>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
src/components/UserError.tsx
Normal file
5
src/components/UserError.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router'
|
||||
|
||||
export function UserErrorComponent({ error }: ErrorComponentProps) {
|
||||
return <ErrorComponent error={error} />
|
||||
}
|
||||
44
src/content/coche.ts
Normal file
44
src/content/coche.ts
Normal file
@ -0,0 +1,44 @@
|
||||
export const dataChipCoche = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Asistencia en viaje las 24h desde el Km 0",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Vehículo de sustitución por accidente y robo",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Daños y robo de equipajes",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Repintado del vehículo",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Error en carga de combustible",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Daños y robo de equipajes",
|
||||
},
|
||||
];
|
||||
export const dataChipMoto = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Tarificación y contratación 100% online",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Con las garantías opcionales más innovadoras",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Total personalización de tu seguro",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Asistencia en viaje las 24h y desde el kilómetro 0",
|
||||
},
|
||||
];
|
||||
178
src/content/decesos.ts
Normal file
178
src/content/decesos.ts
Normal file
@ -0,0 +1,178 @@
|
||||
export const dataChipDecesos = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Contratación 100% online",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Limpieza dental anual gratuita",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Contratable hasta los 80 años",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const dataAccordion = [
|
||||
{
|
||||
icon: "iconify solar--leaf-line-duotone size-5",
|
||||
title: "Servicio de sepelio y asistencia por fallecimiento",
|
||||
key: "sepelio",
|
||||
descripcion:
|
||||
"Prestación del servicio de sepelio: arca, carroza fúnebre, servicios religiosos, capilla ardiente, personal, tasas municipales, certificados, gastos médicos, legales y demás trámites de sepelio, coronas, esquelas, sepultura o nicho y lápidas. Se podrá elegir incineración y otros servicios complementarios tales como coche de acompañantes, tanatorio o sala velatorio, siempre que estén disponibles en el lugar, sin que el coste total del servicio pueda superar el límite máximo de la suma asegurada. También se incluyen servicios de asistencia por fallecimiento, como orientación psicológica al duelo o borrado de la identidad digital del fallecido.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--earth-line-duotone size-5",
|
||||
title: "Asistencia en viaje mundial",
|
||||
key: "viaje",
|
||||
descripcion:
|
||||
"Asistencia en caso de accidente, enfermedad y otros imprevistos que puedan ocurrir en tus viajes. Cobertura nacional e internacional. Ampliable hasta 6 o 12 meses en caso de estancias más largas.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--diploma-line-duotone size-5",
|
||||
title: "Asistencia legal y gestoría",
|
||||
key: "legal",
|
||||
descripcion:
|
||||
"Trámites de gestoría en vida y por fallecimiento, asistencia legal telefónica gratuita, asesoramiento para la elaboración de un testamento online, servicio de redacción y revisión de documentos. Primera consulta en despacho de abogados gratuita y hasta 25% de descuento sobre el resto de actuaciones jurídicas.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--stethoscope-bold-duotone size-5",
|
||||
title: "Prestaciones sanitarias y de cuidado personal",
|
||||
key: "sanidad",
|
||||
descripcion:
|
||||
"Cuadro médico y dental a precios preferentes, limpieza bucal anual gratuita, orientación dietética y nutricional telefónica, videoconsulta médica, chat médico, historial médico en la nube y segunda opinión médica internacional, entre otras prestaciones.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--gps-line-duotone size-5",
|
||||
title: "Repatriación de españoles y extranjeros",
|
||||
key: "repatriacion",
|
||||
descripcion:
|
||||
"Repatriación en caso de fallecimiento del asegurado de nacionalidad extranjera con residencia habitual en España o del asegurado español con residencia habitual en el extranjero y a petición expresa de sus familiares, hasta el aeropuerto internacional más próximo al lugar de inhumación, donde la funeraria que vaya a realizar el servicio se hará cargo del mismo.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--bone-line-duotone size-5",
|
||||
title: "Asistencia a mascotas",
|
||||
key: "mascotas",
|
||||
descripcion:
|
||||
"Asistencia a mascotas de las especies perro y gato, destinadas a compañía o uso doméstico. Incluye servicio de incineración individual, red de veterinarios y servicios de bienestar a precios concertados, asesoramiento telefónico sobre diferentes aspectos relacionados con animales de compañía, asistencia en caso de pérdida o extravío y asistencia legal para dueños de mascotas. Además, con el seguro de decesos también podrás añadir la cobertura de Responsabilidad Civil para perros peligrosos y no peligrosos, obligatorio con la nueva Ley de Bienestar Animal.",
|
||||
},
|
||||
];
|
||||
|
||||
export const servicesAccordion = [
|
||||
{
|
||||
icon: "iconify solar--adhesive-plaster-line-duotone size-5",
|
||||
title: "Acceso a especialistas médicos a precios preferentes",
|
||||
key: "acceso-especialistas",
|
||||
descripcion:
|
||||
"Aunque tu familia goce de una buena salud, es necesario acudir regularmente a revisiones médicas. Con tu seguro de decesos, ponemos a tu disposición la más completa red de centros sanitarios y profesionales médicos a precios preferentes. Se incluyen también acuerdos preferentes en medicina preventiva (reconocimiento médico, test farmacogenético, test de intolerancia alimenticia, test genético preventivo, test PCR Covid-19).",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--heart-pulse-line-duotone size-5",
|
||||
title: "Limpieza dental anual gratuita",
|
||||
key: "viaje",
|
||||
descripcion:
|
||||
"Los dentistas recomiendan una asidua limpieza bucal para el cuidado de tus dientes. Gracias a tu seguro podrás disfrutar de una limpieza bucal al año gratis por cada asegurado incluido en tu póliza, además de otros servicios gratuitos o a precios reducidos.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--diploma-line-duotone size-5",
|
||||
title: "Orientación dietética y nutricional telefónica",
|
||||
key: "orientacion-medica",
|
||||
descripcion:
|
||||
"Una alimentación sana y equilibrada ayuda a mantener la salud y el bienestar, a la vez que favorece la prevención de enfermedades. Si lo necesitas, un especialista en dietética y nutrición te asesorará sobre dietas, hábitos alimenticios, dieta para patologías específicas, alteraciones nutricionales, etc.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--monitor-camera-line-duotone size-5",
|
||||
title: "Videoconsulta médica",
|
||||
key: "videoconsulta-medica",
|
||||
descripcion:
|
||||
"Podrás concertar una cita, estés donde estés, con un equipo médico a través de videollamada, para hacer tus consultas de medicina general, pediatría, psicología, fisioterapia, alergología, traumatología, oftalmología, otorrinolaringología y nutrición y dietética. Con posibilidad de receta electrónica.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--chat-line-line-duotone size-5",
|
||||
title: "Chat médico",
|
||||
key: "duda-medica",
|
||||
descripcion:
|
||||
"Ante cualquier duda médica, no hay nada como poder contactar rápidamente con un profesional. Ponemos a tu disposición un chat sin cita previa, donde podrás realizar consultas médicas, subir analíticas o informes y recibir orientación sobre diagnóstico, tratamientos o prevención de enfermedades. Con posibilidad de receta electrónica.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--letter-opened-line-duotone size-5",
|
||||
title: "Historial médico en la nube",
|
||||
key: "historial",
|
||||
descripcion:
|
||||
"Ponemos a tu disposición una plataforma que te permitirá acceder cómodamente a tu historial médico, en cualquier momento y desde cualquier lugar. Así, los profesionales sanitarios que lo precisen, tendrán acceso a toda la información necesaria, en caso de urgencia o necesidad inminente, tanto en el territorio nacional como en el extranjero.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--globus-line-duotone size-5",
|
||||
title: "Segunda opinión médica internacional",
|
||||
key: "segunda-op-medica",
|
||||
descripcion:
|
||||
"En caso de enfermedad grave, gestionamos la solicitud de una segunda opinión médica, con especialistas de referencia a nivel mundial. ",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--file-line-duotone size-5",
|
||||
title: "Trámites de gestoría en vida",
|
||||
key: "tramites",
|
||||
descripcion:
|
||||
"Presentación telemática de la solicitud y de la documentación requerida por la Seguridad Social para iniciar los trámites de Jubilación Ordinaria y/o Jubilación de Reglamentos o Convenios. Redacción de borrador y asesoramiento sobre el registro de las últimas voluntades vitales anticipadas (atenciones médicas, cuidados paliativos en caso de enfermedades no reversibles). Informe de reputación digital y solicitud de derecho al olvido.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--folder-favourite-bookmark-line-duotone size-5",
|
||||
title: "Testamento online",
|
||||
key: "testamento",
|
||||
descripcion:
|
||||
"¿Has pensado alguna vez en redactar tu testamento pero no sabes cómo hacerlo? Te ofrecemos la ayuda de un abogado especialista en derecho sucesorio, que te ayudará en la redacción de tu testamento, ajustándolo a tu situación personal.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--compass-square-line-duotone size-5",
|
||||
title: "Orientación psicológica al duelo",
|
||||
key: "orientacion",
|
||||
descripcion:
|
||||
"Nunca estamos preparados para despedir a nuestros seres queridos. Te acompañamos en este duro trance con la ayuda de un psicólogo que te prestará su apoyo para afrontar el duelo. En el momento que nos necesites, allí estaremos.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--user-minus-broken size-5",
|
||||
title: "Borrado de identidad digital del fallecido",
|
||||
key: "borrado-id",
|
||||
descripcion:
|
||||
"Ponemos a tu disposición la posibilidad de solicitar la eliminación de la información personal que pueda aparecer sobre la persona fallecida en las redes sociales.",
|
||||
},
|
||||
];
|
||||
|
||||
export const petsCards = [
|
||||
{
|
||||
title: "Servicio de incineración",
|
||||
description:
|
||||
"Gastos de incineración individual, con asesoramiento y coordinación las 24h del día.",
|
||||
icon: "iconify solar--bone-broken",
|
||||
},
|
||||
{
|
||||
title: "Red de veterinarios",
|
||||
description: "Extensa red de veterinarios a precios concertados.",
|
||||
icon: "iconify solar--users-group-two-rounded-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Servicios de bienestar",
|
||||
description:
|
||||
"Red de centros y servicios a precios concertados: peluquerías caninas, tiendas especializadas, localizador GPS.",
|
||||
icon: "iconify solar--jar-of-pills-2-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Residencias caninas",
|
||||
description:
|
||||
"Precios concertados para el cuidado y residencia de la mascota.",
|
||||
icon: "iconify solar--buildings-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Atención telefónica especializada",
|
||||
description:
|
||||
"Asesoramiento sobre hoteles, transporte, criaderos o eventos para animales de compañía. Asistencia en caso de pérdida o extravío.",
|
||||
icon: "iconify solar--phone-calling-rounded-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Asistencia jurídica",
|
||||
description:
|
||||
"Asistencia legal sobre cualquier cuestión relativa a tu mascota.",
|
||||
icon: "iconify solar--file-line-duotone",
|
||||
},
|
||||
];
|
||||
105
src/content/hogar.ts
Normal file
105
src/content/hogar.ts
Normal file
@ -0,0 +1,105 @@
|
||||
export const dataChipHome = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Vivienda habitual y segunda vivienda",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Atención telefónica 24 horas",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Contrata lo necesario",
|
||||
},
|
||||
];
|
||||
|
||||
export const indexCards = [
|
||||
{
|
||||
title: "Atención a medida",
|
||||
icon: "iconify solar--headphones-round-sound-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Líbrate del papeleo",
|
||||
icon: "iconify solar--file-corrupted-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Sin complicaciones",
|
||||
icon: "iconify solar--meditation-round-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Presupuestos adaptables",
|
||||
icon: "iconify solar--wallet-money-line-duotone",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const homeCards = [
|
||||
{
|
||||
title: "Asistencia Bricohogar",
|
||||
description:
|
||||
"Disfruta sin coste de ayuda profesional a domicilio para tareas de mantenimiento e instalación en tu hogar: fontanería, carpintería, electricidad y más.",
|
||||
icon: "iconify solar--settings-linear",
|
||||
},
|
||||
{
|
||||
title: "Asistencia en el hogar",
|
||||
description:
|
||||
"Profesionales cualificados acudirán gratuitamente a tu domicilio para reparaciones urgentes: fontanería, electricidad, cerrajería, albañilería, pintura, persianas o tapicería.",
|
||||
icon: "iconify solar--home-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Asistencia informática",
|
||||
description:
|
||||
"Un experto te asesorará en el uso de herramientas informáticas, resolverá incidencias y te ayudará a configurar tu ordenador, tablet o móvil.",
|
||||
icon: "iconify solar--translation-2-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Asistencia jurídica",
|
||||
description:
|
||||
"Protege a tu familia con asesoría legal completa, tanto judicial como extrajudicial, ante posibles daños personales o materiales.",
|
||||
icon: "iconify solar--case-line-duotone",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const accordionfrequentlyaskedquestions = [
|
||||
{
|
||||
title: "¿Es obligatorio el seguro de hogar?",
|
||||
description:
|
||||
"El seguro de hogar no es obligatorio, sin embargo, se recomienda tener uno. Una vivienda supone una gran inversión y puede sufrir la rotura de una tubería, un incendio o un robo, entre otros imprevistos. Contar con un buen seguro te dará tranquilidad, ya que algunos de estos percances pueden provocar daños muy graves y suponer un importante desembolso económico.",
|
||||
},
|
||||
{
|
||||
title: "¿Es obligatorio contratar un seguro de Hogar con la hipoteca?",
|
||||
description:
|
||||
"A la hora de pedir un préstamo hipotecario, una de las cuestiones que más dudas genera a los propietarios es la de la contratación del seguro de Hogar. La vivienda hipotecada deberá contar con un seguro de protección básico contra incendios, te contamos todo en nuestro blog.",
|
||||
},
|
||||
{
|
||||
title: "¿Qué cubre el seguro de hogar?",
|
||||
description:
|
||||
"Con el Seguro de Hogar de Victoria Seguros tendrás a tu disposición las coberturas más completas del mercado para que siempre te sientas protegido, tales como: Goteras y filtraciones, Desatasco, Fontanería urgente sin daños, Filtraciones por defecto en el sellado de juntas en aparatos sanitarios, Exceso de consumo de agua, Robo en Anexo y Restauración estética para bienes mobiliarios... Además, para asegurar tu tranquilidad, te ofrecemos amplios límites en las coberturas de Robo y Daños por agua. Asimismo, con tu seguro de hogar podrás disfrutar de una selección de servicios tales como: Asistencia en el Hogar e Informática, Bricohogar, Reparación de electrodomésticos de línea blanca, Navegación segura en internet, Línea médica, entre otros.",
|
||||
},
|
||||
{
|
||||
title: "¿Qué cubre la responsabilidad civil de un seguro de hogar?",
|
||||
description:
|
||||
"La cobertura de responsabilidad civil cubre los daños materiales o personales que tanto tú, como tu familia o la propia vivienda, podáis causar accidentalmente a terceras personas. Por ejemplo, si un escape de agua en tu casa afectase a la vivienda del vecino de abajo, la aseguradora se haría cargo del problema ocasionado, abonando los gastos de reparación.",
|
||||
},
|
||||
{
|
||||
title: "¿Qué es el continente en un seguro de hogar?",
|
||||
description:
|
||||
"El continente es toda la estructura de tu vivienda (muros, paredes, techos, suelo), los elementos fijos (puertas, ventanas, persianas, armarios empotrados) y las instalaciones fijas (calefacción, agua, electricidad). También forman parte del continente las construcciones anexas (garaje, piscina, trastero).",
|
||||
},
|
||||
{
|
||||
title: "¿Qué es el contenido en un seguro de hogar?",
|
||||
description:
|
||||
"El contenido está compuesto por los bienes que se encuentran dentro de tu vivienda como los muebles, los electrodomésticos, los aparatos eléctricos... En definitiva, todo aquello que si diésemos la vuelta a la casa y la sacudiésemos, caería al suelo.",
|
||||
},
|
||||
{
|
||||
title: "¿Cuánto cuesta un seguro de hogar?",
|
||||
description:
|
||||
"El Seguro de Hogar se calcula en función de los datos de la vivienda proporcionada: tipo de vivienda, superficie, uso, ubicación, personas que la habitan, año de construcción, elementos de seguridad... Con los datos que nos facilites sobre tu casa te recomendaremos unos capitales de contenido y continente y, dado que los precios varían según los factores mencionados anteriormente, es difícil proporcionar un coste exacto. Sin embargo, el coste promedio de un seguro de hogar online en Victoria Seguros es de 180€/año* (*en función de los datos de la vivienda proporcionada).",
|
||||
},
|
||||
{
|
||||
title: "¿No encuentras las garantías o coberturas que necesitas?",
|
||||
description:
|
||||
"En Victoria Seguros tenemos disponibles más opciones dentro del Seguro de Hogar. Consulta la posibilidad de incluir garantías como Responsabilidad Civil de perros de raza peligrosa o del cazador, coberturas sobre instalaciones solares o piscinas, etc. Contacta con nosotros y estudiaremos tu caso, sin compromiso.",
|
||||
},
|
||||
];
|
||||
32
src/content/home.ts
Normal file
32
src/content/home.ts
Normal file
@ -0,0 +1,32 @@
|
||||
export const homeCards = [
|
||||
{
|
||||
title: "Atencióna medida",
|
||||
description:
|
||||
"Siempre te atenderá la misma persona durante todo el trayecto de tu contrato..",
|
||||
icon: "iconify solar--headphones-round-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Cero complicaciones",
|
||||
description: "Yo me encargo del papeleo, tú solo disfruta de la tranquilidad. ¡Así de fácil!",
|
||||
icon: "iconify solar--face-scan-circle-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Servicios de bienestar",
|
||||
description:
|
||||
"Yo me encargo del papeleo, tú solo disfruta de la tranquilidad. ¡Así de fácil!",
|
||||
icon: "iconify solar--stethoscope-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Confianza total",
|
||||
description:
|
||||
"Te ofrezco asesoramiento personalizado. Aquí no hay sorpresas, solo lo mejor para ti.",
|
||||
icon: "iconify solar--hand-heart-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Siempre contigo",
|
||||
description:
|
||||
"Cuando lo necesites, estaré aquí. Un seguro que te cuida en cada paso del camino..",
|
||||
icon: "iconify solar--phone-calling-rounded-line-duotone",
|
||||
},
|
||||
|
||||
];
|
||||
113
src/content/perros.ts
Normal file
113
src/content/perros.ts
Normal file
@ -0,0 +1,113 @@
|
||||
export const dataChip = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Perros peligrosos",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Varios perros, una póliza",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Hasta 400.000€ Defensa Jurídica",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Hasta 400.000€ Responsabilidad Civil",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Cobertura nacional o europea",
|
||||
},
|
||||
];
|
||||
|
||||
export const dataChipCaballos = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Desde 100€ al año",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Varios caballos, una póliza",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Descuentos por números",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Responsabilidad Civil",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Recogida y destrucción de cadáveres",
|
||||
},
|
||||
];
|
||||
|
||||
export const dataAccordion = [
|
||||
{
|
||||
icon: "iconify solar--hand-money-line-duotone size-5",
|
||||
title: "Responsabilidad Civil General (400.000€)",
|
||||
key: "sepelio",
|
||||
descripcion:
|
||||
"Cubre los daños materiales y personales, así como aquellos perjuicios causados a terceros en su condición de propietario o poseedor del perro.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--hand-money-line-duotone size-5",
|
||||
title: "Gastos de Defensa Jurídica (400.000€)",
|
||||
key: "viaje",
|
||||
descripcion:
|
||||
"Gastos de defensa del propietario del perro derivados de incidentes con terceras personas.",
|
||||
},
|
||||
];
|
||||
|
||||
export const dataAccordionCaballos = [
|
||||
{
|
||||
icon: "iconify solar--shield-up-line-duotone size-5",
|
||||
title: "Garantiza tu protección frente a cualquier incidente",
|
||||
key: "proteccion",
|
||||
descripcion:
|
||||
"Disfruta con tu caballo de los buenos momentos, una competición, un evento o simplemente montando en el campo, y deja los contratiempos en nuestras manos. El Seguro de Caballos de Victoria Seguros te garantiza la responsabilidad civil como propietario, protegiéndote frente a las posibles consecuencias, tanto materiales como personales, que pudiera ocasionar tu caballo a terceros. Asimismo, incluye la garantía de retirada y destrucción de cadáveres, en caso de muerte del animal.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--golf-line-duotone size-5",
|
||||
title: "Ágil y fácil de contratar",
|
||||
key: "agilidad",
|
||||
descripcion:
|
||||
"Nuestro Seguro de Caballos te ofrece personalización y sencillez en su contratación. Por un lado, la personalización, ya que te permite elegir entre sus dos opciones de contratación, en función del capital de responsabilidad civil que desees asegurar (150.000, 300.000 o 600.000 euros) y, por otro lado, la facilidad, al ofrecer precios cerrados con cuatro tarifas que dependen de la edad del animal, así como la posibilidad de incluir todos tus caballos en una sola póliza.Este seguro está dirigido a caballos de entre 1 y 20 años.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--wallet-money-line-duotone",
|
||||
title: "Desde 100 euros al año y con descuentos adicionales",
|
||||
key: "gastos",
|
||||
descripcion:
|
||||
"Tener un caballo es una gran responsabilidad. Sin embargo, desde solo 100 euros al año, podrás tener la tranquilidad de estar protegido ante posibles daños a terceros.Además de tener un precio competitivo, podrás beneficiarte por incluir todos tus caballos en una sola póliza, obteniendo descuentos al asegurar más de cinco equinos en la misma.",
|
||||
},
|
||||
];
|
||||
|
||||
export const petsCards = [
|
||||
{
|
||||
title: "Contrata tu seguro y descarga tu certificado",
|
||||
description:
|
||||
"A través de un sencillo proceso de contratación online podrás obtener tu certificado en tan solo unos minutos y tenerlo siempre a mano cuando lo necesites.",
|
||||
icon: "iconify solar--diploma-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Tranquilidad ante los imprevistos",
|
||||
description:
|
||||
"El Seguro de Responsabilidad Civil de perros cubre los daños que tu mascota ocasione a una tercera persona, así como los gastos de defensa derivados de los mismos.",
|
||||
icon: "iconify solar--like-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Máxima protección",
|
||||
description:
|
||||
"Este seguro te ofrece cobertura en todo el territorio nacional (con posibilidad de ampliar a la Unión Europea) con un capital de 400.000€ por siniestro/año y sin franquicia.",
|
||||
icon: "iconify solar--shield-up-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Tus perros en la misma póliza",
|
||||
description:
|
||||
"Precios concertados para el cuidado y residencia de la mascota.",
|
||||
icon: "iconify solar--buildings-line-duotone",
|
||||
},
|
||||
];
|
||||
74
src/content/salud.ts
Normal file
74
src/content/salud.ts
Normal file
@ -0,0 +1,74 @@
|
||||
export const dataChipHealth = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Respaldo de Caser aseguradora ",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Tratamientos personalizados",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Telemedicina, selfie health...",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "App Salud, cheque dental...",
|
||||
},
|
||||
];
|
||||
|
||||
export const dataCardDemo = [
|
||||
{
|
||||
title: "Un seguro para todas las edades",
|
||||
icon: "iconify solar--heart-pulse-line-duotone size-20",
|
||||
description:
|
||||
"Incluye pruebas, urgencias, especialistas, hospitalización... Y, además, coberturas para el embarazo, posparto y la infancia.",
|
||||
},
|
||||
{
|
||||
title: "Cuadro médico de calidad",
|
||||
icon: "iconify solar--stethoscope-line-duotone size-20",
|
||||
description:
|
||||
"Con más de 45.000 profesionales y 13.000 centros para citas presenciales y telemedicina (llamadas y videollamadas ilimitadas).",
|
||||
},
|
||||
{
|
||||
title: "Ahorro en tus gastos médicos",
|
||||
icon: "iconify solar--wallet-money-line-duotone size-20",
|
||||
description:
|
||||
"10 primeros servicios médicos sin coste por cada asegurado y reembolso en gastos de farmacia y vacunas infantiles.",
|
||||
},
|
||||
];
|
||||
|
||||
export const dataCardHealth = [
|
||||
{
|
||||
title: "Te devolvemos el 50% de lo que te gastes en tu óptica",
|
||||
icon: "iconify solar--heart-pulse-line-duotone size-20",
|
||||
description:
|
||||
"La vista es un elemento fundamental, nuestro sentido más desarrollado. En Victoria Seguros, queremos que tu mirada tenga una graduación inmejorable, por ello, te ayudamos en la compra o renovación de tus gafas y lentes de contacto.",
|
||||
},
|
||||
{
|
||||
title:
|
||||
"Te reembolsamos el 50% de tus gastos en farmacia y vacunas infantiles",
|
||||
icon: "iconify solar--stethoscope-line-duotone size-20",
|
||||
description:
|
||||
"En ocasiones, los medicamentos o vacunas para los peques no están cubiertos por la sanidad pública, lo que supone un gasto extra para tu bolsillo. Nosotros, abaratamos estos costes.",
|
||||
},
|
||||
{
|
||||
title:
|
||||
"Te devolvemos hasta 600€ por la compra de cualquier modalidad de crioconservación de células madre Biocord",
|
||||
icon: "iconify solar--wallet-money-line-duotone size-20",
|
||||
description:
|
||||
"Te reembolsamos hasta 600€ por la compra de cualquier modalidad de crioconservación de células madre Biocord, a través de nuestra plataforma de servicios Caser Más Beneficios.",
|
||||
},
|
||||
{
|
||||
title: "Te regalamos un cheque dental por valor de 100€",
|
||||
icon: "iconify solar--wallet-money-line-duotone size-20",
|
||||
description:
|
||||
"Te regalamos un cheque descuento por asegurado por valor de 100€ en Ortodoncias e Implantes ó 30€ en el resto de tratamientos.",
|
||||
},
|
||||
{
|
||||
title: "Descuento de 50€ en maternidad y reproducción asistida",
|
||||
icon: "iconify solar--wallet-money-line-duotone size-20",
|
||||
description:
|
||||
"Descuento de 50€ en maternidad y reproducción asistida o 20€ en nutrición, genética y prevención para la compra de servicios de salud, bienestar y prevención.",
|
||||
},
|
||||
];
|
||||
107
src/content/vida.ts
Normal file
107
src/content/vida.ts
Normal file
@ -0,0 +1,107 @@
|
||||
export const dataChipVida = [
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Fallecimiento",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Capital hasta 150.000€",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "Victoria Seguros Cuidados",
|
||||
},
|
||||
{
|
||||
label: "iconify solar--shield-check-line-duotone size-5",
|
||||
icon: "25% de descuento en tu seguro",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export const servicesAccordionLife = [
|
||||
{
|
||||
icon: "iconify solar--question-square-line-duotone size-5",
|
||||
title: "¿Qué es un seguro de vida?",
|
||||
key: "acceso-especialistas",
|
||||
descripcion:
|
||||
"El seguro de Vida supone un respaldo económico para los familiares (beneficiarios de la póliza) de los asegurados tras su muerte o su incapacidad, ya que ellos recibirán la suma del capital asegurado cuando el suceso ocurra. Los beneficiarios del seguro, dependiendo de lo que haya estipulado previamente el asegurado con la aseguradora, podrán percibir esta prestación económica a plazos o como un único pago.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--question-square-line-duotone size-5",
|
||||
title: "¿Para qué sirve un seguro de vida?",
|
||||
key: "viaje",
|
||||
descripcion:
|
||||
"Si contratas un seguro de vida, en caso de fallecer o quedar inválido por cualquier causa, ayudarás a tu familia a salir adelante. Contarán con los recursos necesarios para poder seguir pagando la hipoteca, hacer frente a los gastos familiares... y todo será más fácil para ellos. Todos tenemos la responsabilidad de proteger a aquellos que dependen de nosotros.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--question-square-line-duotone size-5",
|
||||
title: "¿Es obligatorio seguro de vida con la hipoteca?",
|
||||
key: "orientacion-medica",
|
||||
descripcion:
|
||||
"No es algo vinculante pero es cierto que el seguro de Vida es muy útil si se tiene una hipoteca o algún préstamo pendiente, ya que, si al asegurado le sucede algo, su familia estará protegida en este sentido, no teniendo que hacer frente a sus deudas. Pero más allá de eso, no hay que olvidar que, si el asegurado fallece o sufre una incapacidad o enfermedad grave y es el principal sustento económico de la familia, sus familiares pueden quedar desprotegidos al no disponer de sus ingresos económicos, ya que éste no podría trabajar. De manera que este seguro siempre es útil, aunque no se tengan deudas pendientes.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--question-square-line-duotone size-5",
|
||||
title: "¿Cómo cobrar un seguro de Vida tras la muerte de un familiar?",
|
||||
key: "videoconsulta-medica",
|
||||
descripcion:
|
||||
"En la actualidad los seguros de Vida constituyen una garantía de protección no sólo para la persona que contrata la póliza, sino para toda su familia. ¿Cómo cobrar un seguro de Vida tras la muerte de un familiar? Te explicamos como en el siguiente post.",
|
||||
},
|
||||
{
|
||||
icon: "iconify solar--question-square-line-duotone size-5",
|
||||
title: "¿Cuáles son las diferentes formas de cobrar un seguro de Vida?",
|
||||
key: "duda-medica",
|
||||
descripcion:
|
||||
"A la hora de contratar un seguro de Vida, el tomador establece en qué forma quiere que el beneficiario cobre la indemnización que le corresponde en caso de que él fallezca. A continuación te explicamos las diferentes formas de cobro de un seguro de Vida.",
|
||||
},
|
||||
];
|
||||
|
||||
export const lifesafeCards = [
|
||||
{
|
||||
title: "Segunda opinión médica",
|
||||
description:
|
||||
"Si quieres confirmar tu diagnóstico, conocer los mejores tratamientos o consultar tus dudas, un médico especialista nacional o internacional revisará tu caso.",
|
||||
icon: "iconify solar--health-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Medicina preventiva",
|
||||
description: "Si tienes dudas, tienes la posibilidad de realizarte un reconocimiento médico completo para prevenir y detectar a tiempo cualquier problema de salud.",
|
||||
icon: "iconify solar--stethoscope-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Teleconsulta",
|
||||
description:
|
||||
"Recibe orientación online con un equipo de profesionales médicos. Una forma rápida, directa y sin esperas de solucionar tus dudas de salud, estés donde estés.",
|
||||
icon: "iconify solar--monitor-camera-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Orientación médica telefónica",
|
||||
description:
|
||||
"Resuelve tus consultas acerca de tu salud o la de tu familia con este servicio telefónico 24h, 365 días al año, atendido por médicos de diferentes especialidades.",
|
||||
icon: "iconify solar--monitor-smartphone-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Servicios Legales",
|
||||
description:
|
||||
"Incluye los servicios de asesoramiento para elaborar tu Testamento online de forma sencilla y Borrado de huella digital para la eliminación de información personal en redes sociales e Internet.",
|
||||
icon: "iconify solar--folder-with-files-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Test genético",
|
||||
description:
|
||||
"Conoce, mediante una simple muestra de saliva, tu propensión a padecer algunas de las enfermedades relacionadas con tu constitución genética.",
|
||||
icon: "iconify solar--dna-bold-duotone",
|
||||
},
|
||||
{
|
||||
title: "Test nutricional",
|
||||
description:
|
||||
"Descubre la dieta que necesitas con un test genético que analiza cómo tu cuerpo asimila los alimentos y cómo influyen en tu salud y peso.",
|
||||
icon: "iconify solar--donut-line-duotone",
|
||||
},
|
||||
{
|
||||
title: "Apoyo en desplazamientos médicos",
|
||||
description:
|
||||
"Te ayudamos con los trámites para recibir tratamiento fuera de España: encontrar al mejor especialista, agendar tus citas o coordinar tu viaje y estancia.",
|
||||
icon: "iconify solar--bus-line-duotone",
|
||||
},
|
||||
];
|
||||
5
src/global-middleware.ts
Normal file
5
src/global-middleware.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { registerGlobalMiddleware } from '@tanstack/react-start'
|
||||
|
||||
registerGlobalMiddleware({
|
||||
middleware: [],
|
||||
})
|
||||
431
src/routeTree.gen.ts
Normal file
431
src/routeTree.gen.ts
Normal file
@ -0,0 +1,431 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
// Import Routes
|
||||
|
||||
import { Route as rootRoute } from './routes/__root'
|
||||
import { Route as IndexImport } from './routes/index'
|
||||
import { Route as FormularioIndexImport } from './routes/formulario/index'
|
||||
import { Route as SegurosVidaImport } from './routes/seguros/vida'
|
||||
import { Route as SegurosSaludImport } from './routes/seguros/salud'
|
||||
import { Route as SegurosHogarImport } from './routes/seguros/hogar'
|
||||
import { Route as SegurosDecesosImport } from './routes/seguros/decesos'
|
||||
import { Route as FormularioPoliticasFormularioImport } from './routes/formulario/politicas-formulario'
|
||||
import { Route as FormularioPoliticasImport } from './routes/formulario/politicas'
|
||||
import { Route as SegurosVehiculosIndexImport } from './routes/seguros/vehiculos/index'
|
||||
import { Route as SegurosMascotasIndexImport } from './routes/seguros/mascotas/index'
|
||||
import { Route as SegurosVehiculosMotoImport } from './routes/seguros/vehiculos/moto'
|
||||
import { Route as SegurosVehiculosCocheImport } from './routes/seguros/vehiculos/coche'
|
||||
import { Route as SegurosMascotasPerrosImport } from './routes/seguros/mascotas/perros'
|
||||
import { Route as SegurosMascotasCaballosImport } from './routes/seguros/mascotas/caballos'
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const FormularioIndexRoute = FormularioIndexImport.update({
|
||||
id: '/formulario/',
|
||||
path: '/formulario/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosVidaRoute = SegurosVidaImport.update({
|
||||
id: '/seguros/vida',
|
||||
path: '/seguros/vida',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosSaludRoute = SegurosSaludImport.update({
|
||||
id: '/seguros/salud',
|
||||
path: '/seguros/salud',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosHogarRoute = SegurosHogarImport.update({
|
||||
id: '/seguros/hogar',
|
||||
path: '/seguros/hogar',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosDecesosRoute = SegurosDecesosImport.update({
|
||||
id: '/seguros/decesos',
|
||||
path: '/seguros/decesos',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const FormularioPoliticasFormularioRoute =
|
||||
FormularioPoliticasFormularioImport.update({
|
||||
id: '/formulario/politicas-formulario',
|
||||
path: '/formulario/politicas-formulario',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const FormularioPoliticasRoute = FormularioPoliticasImport.update({
|
||||
id: '/formulario/politicas',
|
||||
path: '/formulario/politicas',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosVehiculosIndexRoute = SegurosVehiculosIndexImport.update({
|
||||
id: '/seguros/vehiculos/',
|
||||
path: '/seguros/vehiculos/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosMascotasIndexRoute = SegurosMascotasIndexImport.update({
|
||||
id: '/seguros/mascotas/',
|
||||
path: '/seguros/mascotas/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosVehiculosMotoRoute = SegurosVehiculosMotoImport.update({
|
||||
id: '/seguros/vehiculos/moto',
|
||||
path: '/seguros/vehiculos/moto',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosVehiculosCocheRoute = SegurosVehiculosCocheImport.update({
|
||||
id: '/seguros/vehiculos/coche',
|
||||
path: '/seguros/vehiculos/coche',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosMascotasPerrosRoute = SegurosMascotasPerrosImport.update({
|
||||
id: '/seguros/mascotas/perros',
|
||||
path: '/seguros/mascotas/perros',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const SegurosMascotasCaballosRoute = SegurosMascotasCaballosImport.update({
|
||||
id: '/seguros/mascotas/caballos',
|
||||
path: '/seguros/mascotas/caballos',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/formulario/politicas': {
|
||||
id: '/formulario/politicas'
|
||||
path: '/formulario/politicas'
|
||||
fullPath: '/formulario/politicas'
|
||||
preLoaderRoute: typeof FormularioPoliticasImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/formulario/politicas-formulario': {
|
||||
id: '/formulario/politicas-formulario'
|
||||
path: '/formulario/politicas-formulario'
|
||||
fullPath: '/formulario/politicas-formulario'
|
||||
preLoaderRoute: typeof FormularioPoliticasFormularioImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/decesos': {
|
||||
id: '/seguros/decesos'
|
||||
path: '/seguros/decesos'
|
||||
fullPath: '/seguros/decesos'
|
||||
preLoaderRoute: typeof SegurosDecesosImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/hogar': {
|
||||
id: '/seguros/hogar'
|
||||
path: '/seguros/hogar'
|
||||
fullPath: '/seguros/hogar'
|
||||
preLoaderRoute: typeof SegurosHogarImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/salud': {
|
||||
id: '/seguros/salud'
|
||||
path: '/seguros/salud'
|
||||
fullPath: '/seguros/salud'
|
||||
preLoaderRoute: typeof SegurosSaludImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/vida': {
|
||||
id: '/seguros/vida'
|
||||
path: '/seguros/vida'
|
||||
fullPath: '/seguros/vida'
|
||||
preLoaderRoute: typeof SegurosVidaImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/formulario/': {
|
||||
id: '/formulario/'
|
||||
path: '/formulario'
|
||||
fullPath: '/formulario'
|
||||
preLoaderRoute: typeof FormularioIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/mascotas/caballos': {
|
||||
id: '/seguros/mascotas/caballos'
|
||||
path: '/seguros/mascotas/caballos'
|
||||
fullPath: '/seguros/mascotas/caballos'
|
||||
preLoaderRoute: typeof SegurosMascotasCaballosImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/mascotas/perros': {
|
||||
id: '/seguros/mascotas/perros'
|
||||
path: '/seguros/mascotas/perros'
|
||||
fullPath: '/seguros/mascotas/perros'
|
||||
preLoaderRoute: typeof SegurosMascotasPerrosImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/vehiculos/coche': {
|
||||
id: '/seguros/vehiculos/coche'
|
||||
path: '/seguros/vehiculos/coche'
|
||||
fullPath: '/seguros/vehiculos/coche'
|
||||
preLoaderRoute: typeof SegurosVehiculosCocheImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/vehiculos/moto': {
|
||||
id: '/seguros/vehiculos/moto'
|
||||
path: '/seguros/vehiculos/moto'
|
||||
fullPath: '/seguros/vehiculos/moto'
|
||||
preLoaderRoute: typeof SegurosVehiculosMotoImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/mascotas/': {
|
||||
id: '/seguros/mascotas/'
|
||||
path: '/seguros/mascotas'
|
||||
fullPath: '/seguros/mascotas'
|
||||
preLoaderRoute: typeof SegurosMascotasIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/seguros/vehiculos/': {
|
||||
id: '/seguros/vehiculos/'
|
||||
path: '/seguros/vehiculos'
|
||||
fullPath: '/seguros/vehiculos'
|
||||
preLoaderRoute: typeof SegurosVehiculosIndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export the route tree
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/formulario/politicas': typeof FormularioPoliticasRoute
|
||||
'/formulario/politicas-formulario': typeof FormularioPoliticasFormularioRoute
|
||||
'/seguros/decesos': typeof SegurosDecesosRoute
|
||||
'/seguros/hogar': typeof SegurosHogarRoute
|
||||
'/seguros/salud': typeof SegurosSaludRoute
|
||||
'/seguros/vida': typeof SegurosVidaRoute
|
||||
'/formulario': typeof FormularioIndexRoute
|
||||
'/seguros/mascotas/caballos': typeof SegurosMascotasCaballosRoute
|
||||
'/seguros/mascotas/perros': typeof SegurosMascotasPerrosRoute
|
||||
'/seguros/vehiculos/coche': typeof SegurosVehiculosCocheRoute
|
||||
'/seguros/vehiculos/moto': typeof SegurosVehiculosMotoRoute
|
||||
'/seguros/mascotas': typeof SegurosMascotasIndexRoute
|
||||
'/seguros/vehiculos': typeof SegurosVehiculosIndexRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/formulario/politicas': typeof FormularioPoliticasRoute
|
||||
'/formulario/politicas-formulario': typeof FormularioPoliticasFormularioRoute
|
||||
'/seguros/decesos': typeof SegurosDecesosRoute
|
||||
'/seguros/hogar': typeof SegurosHogarRoute
|
||||
'/seguros/salud': typeof SegurosSaludRoute
|
||||
'/seguros/vida': typeof SegurosVidaRoute
|
||||
'/formulario': typeof FormularioIndexRoute
|
||||
'/seguros/mascotas/caballos': typeof SegurosMascotasCaballosRoute
|
||||
'/seguros/mascotas/perros': typeof SegurosMascotasPerrosRoute
|
||||
'/seguros/vehiculos/coche': typeof SegurosVehiculosCocheRoute
|
||||
'/seguros/vehiculos/moto': typeof SegurosVehiculosMotoRoute
|
||||
'/seguros/mascotas': typeof SegurosMascotasIndexRoute
|
||||
'/seguros/vehiculos': typeof SegurosVehiculosIndexRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/formulario/politicas': typeof FormularioPoliticasRoute
|
||||
'/formulario/politicas-formulario': typeof FormularioPoliticasFormularioRoute
|
||||
'/seguros/decesos': typeof SegurosDecesosRoute
|
||||
'/seguros/hogar': typeof SegurosHogarRoute
|
||||
'/seguros/salud': typeof SegurosSaludRoute
|
||||
'/seguros/vida': typeof SegurosVidaRoute
|
||||
'/formulario/': typeof FormularioIndexRoute
|
||||
'/seguros/mascotas/caballos': typeof SegurosMascotasCaballosRoute
|
||||
'/seguros/mascotas/perros': typeof SegurosMascotasPerrosRoute
|
||||
'/seguros/vehiculos/coche': typeof SegurosVehiculosCocheRoute
|
||||
'/seguros/vehiculos/moto': typeof SegurosVehiculosMotoRoute
|
||||
'/seguros/mascotas/': typeof SegurosMascotasIndexRoute
|
||||
'/seguros/vehiculos/': typeof SegurosVehiculosIndexRoute
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/formulario/politicas'
|
||||
| '/formulario/politicas-formulario'
|
||||
| '/seguros/decesos'
|
||||
| '/seguros/hogar'
|
||||
| '/seguros/salud'
|
||||
| '/seguros/vida'
|
||||
| '/formulario'
|
||||
| '/seguros/mascotas/caballos'
|
||||
| '/seguros/mascotas/perros'
|
||||
| '/seguros/vehiculos/coche'
|
||||
| '/seguros/vehiculos/moto'
|
||||
| '/seguros/mascotas'
|
||||
| '/seguros/vehiculos'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/formulario/politicas'
|
||||
| '/formulario/politicas-formulario'
|
||||
| '/seguros/decesos'
|
||||
| '/seguros/hogar'
|
||||
| '/seguros/salud'
|
||||
| '/seguros/vida'
|
||||
| '/formulario'
|
||||
| '/seguros/mascotas/caballos'
|
||||
| '/seguros/mascotas/perros'
|
||||
| '/seguros/vehiculos/coche'
|
||||
| '/seguros/vehiculos/moto'
|
||||
| '/seguros/mascotas'
|
||||
| '/seguros/vehiculos'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/formulario/politicas'
|
||||
| '/formulario/politicas-formulario'
|
||||
| '/seguros/decesos'
|
||||
| '/seguros/hogar'
|
||||
| '/seguros/salud'
|
||||
| '/seguros/vida'
|
||||
| '/formulario/'
|
||||
| '/seguros/mascotas/caballos'
|
||||
| '/seguros/mascotas/perros'
|
||||
| '/seguros/vehiculos/coche'
|
||||
| '/seguros/vehiculos/moto'
|
||||
| '/seguros/mascotas/'
|
||||
| '/seguros/vehiculos/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
FormularioPoliticasRoute: typeof FormularioPoliticasRoute
|
||||
FormularioPoliticasFormularioRoute: typeof FormularioPoliticasFormularioRoute
|
||||
SegurosDecesosRoute: typeof SegurosDecesosRoute
|
||||
SegurosHogarRoute: typeof SegurosHogarRoute
|
||||
SegurosSaludRoute: typeof SegurosSaludRoute
|
||||
SegurosVidaRoute: typeof SegurosVidaRoute
|
||||
FormularioIndexRoute: typeof FormularioIndexRoute
|
||||
SegurosMascotasCaballosRoute: typeof SegurosMascotasCaballosRoute
|
||||
SegurosMascotasPerrosRoute: typeof SegurosMascotasPerrosRoute
|
||||
SegurosVehiculosCocheRoute: typeof SegurosVehiculosCocheRoute
|
||||
SegurosVehiculosMotoRoute: typeof SegurosVehiculosMotoRoute
|
||||
SegurosMascotasIndexRoute: typeof SegurosMascotasIndexRoute
|
||||
SegurosVehiculosIndexRoute: typeof SegurosVehiculosIndexRoute
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
FormularioPoliticasRoute: FormularioPoliticasRoute,
|
||||
FormularioPoliticasFormularioRoute: FormularioPoliticasFormularioRoute,
|
||||
SegurosDecesosRoute: SegurosDecesosRoute,
|
||||
SegurosHogarRoute: SegurosHogarRoute,
|
||||
SegurosSaludRoute: SegurosSaludRoute,
|
||||
SegurosVidaRoute: SegurosVidaRoute,
|
||||
FormularioIndexRoute: FormularioIndexRoute,
|
||||
SegurosMascotasCaballosRoute: SegurosMascotasCaballosRoute,
|
||||
SegurosMascotasPerrosRoute: SegurosMascotasPerrosRoute,
|
||||
SegurosVehiculosCocheRoute: SegurosVehiculosCocheRoute,
|
||||
SegurosVehiculosMotoRoute: SegurosVehiculosMotoRoute,
|
||||
SegurosMascotasIndexRoute: SegurosMascotasIndexRoute,
|
||||
SegurosVehiculosIndexRoute: SegurosVehiculosIndexRoute,
|
||||
}
|
||||
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
|
||||
/* ROUTE_MANIFEST_START
|
||||
{
|
||||
"routes": {
|
||||
"__root__": {
|
||||
"filePath": "__root.tsx",
|
||||
"children": [
|
||||
"/",
|
||||
"/formulario/politicas",
|
||||
"/formulario/politicas-formulario",
|
||||
"/seguros/decesos",
|
||||
"/seguros/hogar",
|
||||
"/seguros/salud",
|
||||
"/seguros/vida",
|
||||
"/formulario/",
|
||||
"/seguros/mascotas/caballos",
|
||||
"/seguros/mascotas/perros",
|
||||
"/seguros/vehiculos/coche",
|
||||
"/seguros/vehiculos/moto",
|
||||
"/seguros/mascotas/",
|
||||
"/seguros/vehiculos/"
|
||||
]
|
||||
},
|
||||
"/": {
|
||||
"filePath": "index.tsx"
|
||||
},
|
||||
"/formulario/politicas": {
|
||||
"filePath": "formulario/politicas.tsx"
|
||||
},
|
||||
"/formulario/politicas-formulario": {
|
||||
"filePath": "formulario/politicas-formulario.tsx"
|
||||
},
|
||||
"/seguros/decesos": {
|
||||
"filePath": "seguros/decesos.tsx"
|
||||
},
|
||||
"/seguros/hogar": {
|
||||
"filePath": "seguros/hogar.tsx"
|
||||
},
|
||||
"/seguros/salud": {
|
||||
"filePath": "seguros/salud.tsx"
|
||||
},
|
||||
"/seguros/vida": {
|
||||
"filePath": "seguros/vida.tsx"
|
||||
},
|
||||
"/formulario/": {
|
||||
"filePath": "formulario/index.tsx"
|
||||
},
|
||||
"/seguros/mascotas/caballos": {
|
||||
"filePath": "seguros/mascotas/caballos.tsx"
|
||||
},
|
||||
"/seguros/mascotas/perros": {
|
||||
"filePath": "seguros/mascotas/perros.tsx"
|
||||
},
|
||||
"/seguros/vehiculos/coche": {
|
||||
"filePath": "seguros/vehiculos/coche.tsx"
|
||||
},
|
||||
"/seguros/vehiculos/moto": {
|
||||
"filePath": "seguros/vehiculos/moto.tsx"
|
||||
},
|
||||
"/seguros/mascotas/": {
|
||||
"filePath": "seguros/mascotas/index.tsx"
|
||||
},
|
||||
"/seguros/vehiculos/": {
|
||||
"filePath": "seguros/vehiculos/index.tsx"
|
||||
}
|
||||
}
|
||||
}
|
||||
ROUTE_MANIFEST_END */
|
||||
27
src/router.tsx
Normal file
27
src/router.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
import { DefaultCatchBoundary } from "./components/DefaultCatchBoundary";
|
||||
import { routerWithQueryClient } from "@tanstack/react-router-with-query";
|
||||
import { NotFound } from "./components/NotFound";
|
||||
|
||||
export function createRouter() {
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
return routerWithQueryClient(
|
||||
createTanStackRouter({
|
||||
routeTree,
|
||||
context: { queryClient },
|
||||
defaultPreload: "intent",
|
||||
scrollRestoration: true,
|
||||
defaultErrorComponent: DefaultCatchBoundary,
|
||||
defaultNotFoundComponent: () => <NotFound />,
|
||||
}),
|
||||
queryClient
|
||||
);
|
||||
}
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: ReturnType<typeof createRouter>;
|
||||
}
|
||||
}
|
||||
110
src/routes/__root.tsx
Normal file
110
src/routes/__root.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
HeadContent,
|
||||
Outlet,
|
||||
Scripts,
|
||||
createRootRouteWithContext,
|
||||
} from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import * as React from "react";
|
||||
import { Toaster } from "sonner";
|
||||
import { DefaultCatchBoundary } from "~/components/DefaultCatchBoundary";
|
||||
import { Footer } from "~/components/Footer";
|
||||
import BasicNavbar from "~/components/NavBarComponents";
|
||||
import { NotFound } from "~/components/NotFound";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import { seo } from "~/utils/seo";
|
||||
import { CookiesProvider, useCookies } from "react-cookie";
|
||||
import Clarity from "@microsoft/clarity";
|
||||
// import { createServerFn } from "@tanstack/react-start";
|
||||
// import { getCookie } from "@tanstack/react-start/server";
|
||||
import CookieConsent from "~/components/Cookies";
|
||||
// const cookieValue = createServerFn().s(() => getCookie("cookie-vs"));
|
||||
|
||||
export const Route = createRootRouteWithContext<{
|
||||
queryClient: QueryClient;
|
||||
}>()({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol `,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
// beforeLoad: async () => {
|
||||
// const cookieV = await cookieValue();
|
||||
// return {
|
||||
// haveCookies: cookieV,
|
||||
// };
|
||||
// },
|
||||
errorComponent: (props) => {
|
||||
return (
|
||||
<RootDocument>
|
||||
<DefaultCatchBoundary {...props} />
|
||||
</RootDocument>
|
||||
);
|
||||
},
|
||||
notFoundComponent: () => <NotFound />,
|
||||
component: RootComponent,
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
return (
|
||||
<CookiesProvider>
|
||||
<RootDocument>
|
||||
<Toaster position="top-center" richColors />
|
||||
<Outlet />
|
||||
</RootDocument>
|
||||
</CookiesProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
const projectId = "qxxlp3g93r";
|
||||
Clarity.init(projectId);
|
||||
// const { haveCookies } = Route.useRouteContext();
|
||||
// console.log(haveCookies);
|
||||
// if (haveCookies === "false") {
|
||||
// Clarity.consent(false);
|
||||
// }
|
||||
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<HeadContent />
|
||||
</head>
|
||||
<body>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<div className="flex-1">
|
||||
<BasicNavbar />
|
||||
{children}
|
||||
<div className="sticky bottom-0 z-50">
|
||||
<CookieConsent />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
<TanStackRouterDevtools position="bottom-right" />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
242
src/routes/formulario/index.tsx
Normal file
242
src/routes/formulario/index.tsx
Normal file
@ -0,0 +1,242 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Form } from "@heroui/form";
|
||||
import { Input, Textarea } from "@heroui/input";
|
||||
import { Select, SelectItem } from "@heroui/select";
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Button } from "@heroui/button";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import axios from "axios";
|
||||
import { z } from "zod";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { Checkbox } from "@heroui/checkbox";
|
||||
|
||||
const searchSchema = z.object({
|
||||
seguro: z.string().catch(""),
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/formulario/")({
|
||||
validateSearch: zodValidator(searchSchema),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
interface PropsForm {
|
||||
// TODO: Replace zod infer
|
||||
name?: string;
|
||||
time?: string;
|
||||
description?: string;
|
||||
email?: string;
|
||||
telefono?: string;
|
||||
}
|
||||
|
||||
const seguroItems = {
|
||||
vehiculo: [
|
||||
"Número de matricula",
|
||||
"Compañía anterior",
|
||||
"5 últimos dígitos póliza anterior",
|
||||
"Fecha de nacimiento",
|
||||
"Fecha de carnet",
|
||||
"Código postal",
|
||||
],
|
||||
vida: ["Altura", "Fecha de nacimiento", "Sexo", "Peso"],
|
||||
decesos: [
|
||||
"Fecha de nacimiento de los asegurados",
|
||||
"Número de asegurados",
|
||||
"Código postal",
|
||||
],
|
||||
mascotas: [
|
||||
"Fecha de nacimiento de la mascota",
|
||||
"Código postal",
|
||||
"Número de chip",
|
||||
],
|
||||
};
|
||||
|
||||
function RouteComponent() {
|
||||
const navigate = Route.useNavigate();
|
||||
const { seguro } = Route.useSearch();
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationKey: ["send-email"],
|
||||
mutationFn: async (data: PropsForm) => {
|
||||
const { name, time, description, email, telefono } = data;
|
||||
await axios.post(
|
||||
"https://sender.p3n4lv3r.es/sender",
|
||||
{
|
||||
to: "p3n4lv3r@gmail.com",
|
||||
subject: "Correo decesos",
|
||||
message: `
|
||||
<p>Nombre: ${name} </p>
|
||||
<p>Horario: ${time} </p>
|
||||
<br />
|
||||
<p>Descripción: ${description} </p>
|
||||
<p>Teléfono: ${telefono}</p>
|
||||
<p>Email: ${email} </p>
|
||||
`,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success("Gracias por contactar 📬", { id: "email-toast" });
|
||||
navigate({
|
||||
to: "/",
|
||||
viewTransition: true,
|
||||
replace: true,
|
||||
})
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("Error al enviar petición", { id: "email-toast" });
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(event.currentTarget);
|
||||
|
||||
const name = formData.get("nombre") as string;
|
||||
const time = formData.get("horario") as string;
|
||||
const description = formData.get("descripcion") as string;
|
||||
const email = formData.get("email") as string;
|
||||
const telefono = formData.get("telefono") as string;
|
||||
toast.loading("Enviando", { id: "email-toast" });
|
||||
mutate({ name, time, description, email, telefono } satisfies PropsForm);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col justify-center items-center gap-4 mx-4 mt-2">
|
||||
<p className="text-xl text-secondary">
|
||||
Formulario de{" "}
|
||||
<span className="p-2 text-primary-500 bg-primary-100 rounded-lg ">
|
||||
{seguro || "contacto"}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-sm text-default-500 mb-2">
|
||||
Cuentanos lo que necesitas. Te ayudamos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-center items-center my-2 px-4 w-full">
|
||||
<Card className="w-full max-w-2xl">
|
||||
<CardHeader className="justify-end pb-0">
|
||||
<p className="text-tiny">
|
||||
<span className="text-danger">*</span> campos obligatorios
|
||||
</p>
|
||||
</CardHeader>
|
||||
<Form validationBehavior="native" className="" onSubmit={onSubmit}>
|
||||
<CardBody className="grid md:grid-cols-1 gap-3 ">
|
||||
<Input name="nombre" label="Nombre" isRequired />
|
||||
<div className="grid sm:grid-cols-2 gap-2">
|
||||
<Input name="email" label="Email" />
|
||||
<Input name="telefono" label="Teléfono" isRequired />
|
||||
</div>
|
||||
<Textarea name="descripcion" label="Descripción" isRequired />
|
||||
<div className="justify-end flex p-2">
|
||||
<Checkbox name="acepto" isRequired size="sm" defaultSelected>
|
||||
Acepto la política de{" "}
|
||||
</Checkbox>
|
||||
<Link
|
||||
to="/formulario/politicas-formulario"
|
||||
className="text-primary text-sm ml-1"
|
||||
>
|
||||
privacidad
|
||||
</Link>
|
||||
</div>
|
||||
<p className="text-tiny text-default-500">
|
||||
¿ Prefiere tener un horario de contacto ?
|
||||
</p>
|
||||
<Select
|
||||
name="horario"
|
||||
label="Horario"
|
||||
defaultSelectedKeys={["mor"]}
|
||||
>
|
||||
<SelectItem key="9:30 - 14:00">Mañana 9:30 - 14:00</SelectItem>
|
||||
<SelectItem key="16:00 - 20:00">Tarde 16:00 - 20:00</SelectItem>
|
||||
</Select>
|
||||
</CardBody>
|
||||
<CardFooter className="justify-between gap-4">
|
||||
<Button
|
||||
type="submit"
|
||||
color="danger"
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/",
|
||||
viewTransition: true,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
Atrás
|
||||
</Button>
|
||||
<Button type="submit" isLoading={isPending}>
|
||||
Enviar
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Form>
|
||||
</Card>
|
||||
<div className="p-2 mt-2 rounded-lg bg-white">
|
||||
{seguro && (
|
||||
<p className="font-semibold mb-2">Recomendamos tener a mano:</p>
|
||||
)}
|
||||
<div className="text-sm">
|
||||
{seguro === "vehiculo" && (
|
||||
<div>
|
||||
<ul className="grid gap-2">
|
||||
{seguroItems?.vehiculo.map((e) => (
|
||||
<li className="flex items-center justify-start">
|
||||
{" "}
|
||||
<span className="iconify size-4 solar--alt-arrow-right-outline" />
|
||||
{e}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{seguro === "vida" && (
|
||||
<div>
|
||||
<ul className="grid gap-2">
|
||||
{seguroItems?.vida.map((e) => (
|
||||
<li className="flex items-center justify-start">
|
||||
{" "}
|
||||
<span className="iconify size-4 solar--alt-arrow-right-outline" />
|
||||
{e}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{(seguro === "decesos" || seguro === "salud") && (
|
||||
<div>
|
||||
<ul className="grid gap-2">
|
||||
{seguroItems?.decesos.map((e) => (
|
||||
<li className="flex items-center justify-start">
|
||||
{" "}
|
||||
<span className="iconify size-4 solar--alt-arrow-right-outline" />
|
||||
{e}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{seguro === "mascotas" && (
|
||||
<div className="p-2 bg-default-500">
|
||||
<ul className="grid gap-2">
|
||||
{seguroItems?.mascotas.map((e) => (
|
||||
<li className="flex items-center justify-start">
|
||||
{" "}
|
||||
<span className="iconify size-4 solar--alt-arrow-right-outline" />
|
||||
{e}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
91
src/routes/formulario/politicas-formulario.tsx
Normal file
91
src/routes/formulario/politicas-formulario.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/formulario/politicas-formulario")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="text-gray-800 bg-gray-50 px-6 py-10">
|
||||
<main className="max-w-4xl mx-auto space-y-10">
|
||||
<h1 className="text-4xl font-bold text-blue-900">Política de Privacidad del Formulario</h1>
|
||||
|
||||
<section className="space-y-4 text-lg leading-relaxed">
|
||||
<p>
|
||||
En el sitio web <strong>www.victoriaseguros.es</strong> nos comprometemos a proteger tu privacidad. Esta política explica cómo
|
||||
recogemos, tratamos y protegemos tus datos personales, en cumplimiento del Reglamento (UE) 2016/679 (RGPD), la Ley Orgánica 3/2018 (LOPDGDD)
|
||||
y la Ley 34/2002 (LSSI-CE).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Datos recopilados a través del formulario</h2>
|
||||
<p>
|
||||
Cuando completas nuestro formulario, recopilamos los siguientes datos personales:
|
||||
</p>
|
||||
<ul className="list-disc list-inside pl-2 space-y-1">
|
||||
<li>Nombre</li>
|
||||
<li>Correo electrónico</li>
|
||||
<li>Teléfono</li>
|
||||
</ul>
|
||||
<p>
|
||||
La finalidad del tratamiento es poder gestionar y responder a tus solicitudes de información, así como realizar un seguimiento de tu interés.
|
||||
La base legal es tu consentimiento explícito, otorgado al enviar el formulario.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Uso de Mailjet como encargado del tratamiento</h2>
|
||||
<p>
|
||||
Utilizamos la plataforma <strong>Mailjet</strong> para gestionar el envío de comunicaciones relacionadas con el formulario. Mailjet actúa como
|
||||
encargado del tratamiento de los datos y cumple con los estándares europeos de protección de datos.
|
||||
Puedes consultar su política de privacidad aquí:
|
||||
<a
|
||||
href="https://www.mailjet.com/security-privacy/"
|
||||
className="text-blue-600 underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://www.mailjet.com/security-privacy/
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Plazo de conservación</h2>
|
||||
<p>
|
||||
Conservaremos tus datos únicamente durante el tiempo necesario para atender tu solicitud o mientras mantengamos comunicaciones derivadas del interés mostrado. Posteriormente, los datos serán eliminados de forma segura.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Derechos de protección de datos</h2>
|
||||
<p>
|
||||
Puedes ejercer tus derechos de acceso, rectificación, supresión, limitación del tratamiento, portabilidad y oposición, enviando una solicitud
|
||||
a <strong>info@victoriaseguros.es</strong>, adjuntando una copia de tu documento de identidad.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Medidas de seguridad</h2>
|
||||
<p>
|
||||
Aplicamos medidas técnicas y organizativas apropiadas para garantizar la seguridad y confidencialidad de tus datos, evitando su pérdida, mal uso o acceso no autorizado.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="pt-6 border-t space-y-2">
|
||||
<h2 className="text-xl font-semibold mb-4">Contacto del responsable del tratamiento</h2>
|
||||
<div className="space-y-1 text-gray-700 text-base">
|
||||
<p><strong>Dirección:</strong> Calle Mayor 8, bajo · 03190 Pilar de la Horadada, Alicante</p>
|
||||
<p><strong>Teléfono:</strong> +34 633 620 767</p>
|
||||
<p><strong>Email:</strong> info@victoriaseguros.es</p>
|
||||
</div>
|
||||
<div className="mt-4 space-y-1 text-sm text-gray-600">
|
||||
<p><strong>Lun - Vie:</strong> 09:00 - 21:00</p>
|
||||
<p><strong>Sáb - Dom:</strong> Cerrado</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
110
src/routes/formulario/politicas.tsx
Normal file
110
src/routes/formulario/politicas.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/formulario/politicas")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="text-gray-800 bg-gray-50 px-6 py-10">
|
||||
<main className="max-w-4xl mx-auto space-y-10">
|
||||
<h1 className="text-4xl font-bold text-blue-900">Política de Cookies</h1>
|
||||
|
||||
<section className="space-y-4 text-lg leading-relaxed">
|
||||
<p>
|
||||
En el sitio web <strong>www.victoriaseguros.es</strong>, utilizamos cookies para mejorar la experiencia de navegación,
|
||||
analizar el uso del sitio y mostrar contenido personalizado.
|
||||
</p>
|
||||
<p>
|
||||
Esta política cumple con la Ley 34/2002 (LSSI-CE), el Reglamento (UE) 2016/679 (RGPD) y la Ley Orgánica 3/2018 (LOPDGDD).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">¿Qué son las cookies?</h2>
|
||||
<p>
|
||||
Son pequeños archivos de texto que se almacenan en tu navegador cuando visitas una página web. Sirven para recordar
|
||||
información relevante como tus preferencias, idioma o secciones más visitadas.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Tipos de cookies que utilizamos</h2>
|
||||
<div>
|
||||
<h3 className="font-medium">Según la titularidad</h3>
|
||||
<ul className="list-disc list-inside pl-2 space-y-1">
|
||||
<li><strong>Propias:</strong> gestionadas directamente por nosotros.</li>
|
||||
<li><strong>De terceros:</strong> gestionadas por servicios externos como Microsoft Clarity.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium">Según su finalidad</h3>
|
||||
<ul className="list-disc list-inside pl-2 space-y-1">
|
||||
<li><strong>Técnicas:</strong> necesarias para el funcionamiento básico del sitio.</li>
|
||||
<li><strong>Preferencias:</strong> recuerdan tus configuraciones personales.</li>
|
||||
<li><strong>Analíticas:</strong> recopilan información sobre el uso del sitio para mejorarlo.</li>
|
||||
<li><strong>Publicitarias:</strong> permiten mostrar anuncios adaptados a tus intereses.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium">Según el tiempo de conservación</h3>
|
||||
<ul className="list-disc list-inside pl-2 space-y-1">
|
||||
<li><strong>Sesión:</strong> se eliminan al cerrar el navegador.</li>
|
||||
<li><strong>Persistentes:</strong> permanecen por un periodo definido.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Cookies de terceros: Microsoft Clarity</h2>
|
||||
<p>
|
||||
Utilizamos <strong>Microsoft Clarity</strong> para entender cómo interactúan los usuarios con nuestro sitio mediante mapas de calor y grabaciones anónimas.
|
||||
Estas cookies analíticas se instalan <strong>solo con tu consentimiento explícito</strong>.
|
||||
</p>
|
||||
<p>
|
||||
Más información sobre la política de privacidad de Clarity: {' '}
|
||||
<a
|
||||
href="https://privacy.microsoft.com/es-es/privacystatement"
|
||||
className="text-blue-600 underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://privacy.microsoft.com/es-es/privacystatement
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">Consentimiento y gestión</h2>
|
||||
<p>
|
||||
Al visitar nuestro sitio por primera vez, verás un banner donde puedes aceptar, rechazar o configurar el uso de cookies.
|
||||
Puedes modificar tus preferencias en cualquier momento desde el enlace "Configuración de cookies" en el pie de página.
|
||||
</p>
|
||||
<p>
|
||||
Además, puedes gestionar o eliminar cookies desde tu navegador. Consulta la documentación de tu navegador para más información.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">¿Qué sucede si rechazas las cookies?</h2>
|
||||
<p>
|
||||
Algunas funcionalidades del sitio podrían verse afectadas, como el acceso a áreas privadas, formularios o contenidos personalizados.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="pt-6 border-t space-y-2">
|
||||
<h2 className="text-xl font-semibold mb-4">Contacto</h2>
|
||||
<div className="space-y-1 text-gray-700 text-base">
|
||||
<p><strong>Dirección:</strong> Calle Mayor 8, bajo · 03190 Pilar de la Horadada, Alicante</p>
|
||||
<p><strong>Teléfono:</strong> +34 633 620 767</p>
|
||||
<p><strong>Email:</strong> info@victoriaseguros.es</p>
|
||||
</div>
|
||||
<div className="mt-4 space-y-1 text-sm text-gray-600">
|
||||
<p><strong>Lun - Vie:</strong> 09:00 - 21:00</p>
|
||||
<p><strong>Sáb - Dom:</strong> Cerrado</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
349
src/routes/index.tsx
Normal file
349
src/routes/index.tsx
Normal file
@ -0,0 +1,349 @@
|
||||
import { createFileRoute, Link, Outlet } from "@tanstack/react-router";
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Button } from "@heroui/button";
|
||||
import { Image } from "@heroui/image";
|
||||
import { indexCards } from "~/content/hogar";
|
||||
import { seo } from "~/utils/seo";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import RotatingText from "../TextAnimations/RotatingText/RotatingText";
|
||||
import CountUp from "~/TextAnimations/CountUp/CountUp";
|
||||
import { motion } from "framer-motion";
|
||||
import { Chip } from "@heroui/chip";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol`,
|
||||
keywords:
|
||||
"seguros, alicante, murcia, costa, decesos, vida, salud, muerte, ataud",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: MainComponent,
|
||||
});
|
||||
|
||||
const insuranceCard = [
|
||||
{
|
||||
name: "Decesos",
|
||||
icon: "iconify solar--leaf-line-duotone size-14",
|
||||
link: "/seguros/decesos",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Hogar",
|
||||
icon: "iconify solar--key-line-duotone size-14",
|
||||
link: "/seguros/hogar",
|
||||
},
|
||||
{
|
||||
name: "Vehículos",
|
||||
icon: "iconify solar--wheel-angle-line-duotone size-14",
|
||||
link: "/seguros/vehiculos/",
|
||||
},
|
||||
{
|
||||
name: "Vida",
|
||||
icon: "iconify solar--hand-heart-line-duotone size-14",
|
||||
link: "/seguros/vida",
|
||||
},
|
||||
{
|
||||
name: "Mascotas",
|
||||
icon: "iconify solar--paw-line-duotone size-14",
|
||||
link: "/seguros/mascotas",
|
||||
},
|
||||
{
|
||||
name: "Salud",
|
||||
icon: "iconify solar--heart-pulse-2-line-duotone size-14",
|
||||
link: "/seguros/salud",
|
||||
},
|
||||
];
|
||||
|
||||
export function MainComponent() {
|
||||
const waUrl = `https://wa.me/${+34633620767}`;
|
||||
const navigate = Route.useNavigate();
|
||||
return (
|
||||
<main className="px-2 md:px-8 lg:px-14 min-h-dvh ">
|
||||
<div className="main-lay rounded py-10">
|
||||
<div className="min-h-80 rounded-xl w-full flex justify-center items-center ">
|
||||
<Card
|
||||
className="bg-inherit rounded-lg w-full max-w-7xl"
|
||||
shadow="none"
|
||||
>
|
||||
<CardBody className="overflow-hidden">
|
||||
<h1 className="text-4xl font-bold text-center py-6 flex flex-wrap items-center gap-2 justify-center">
|
||||
Victoria{" "}
|
||||
{/* <span className="text-default-100 bg-primary px-2 py-2 rounded-lg">
|
||||
seguros
|
||||
</span> */}
|
||||
<RotatingText
|
||||
texts={["Seguros", "Vida", "Salud"]}
|
||||
mainClassName="sm:px-2 min-w-40 overflow-hidden justify-center rounded-lg py-2 text-default-100 bg-primary-500 font-semibold text-white"
|
||||
staggerFrom={"last"}
|
||||
initial={{ y: "100%" }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: "-120%" }}
|
||||
staggerDuration={0.025}
|
||||
splitLevelClassName="overflow-hidden pb-0.5 sm:pb-1 md:pb-1"
|
||||
transition={{
|
||||
type: "spring",
|
||||
damping: 30,
|
||||
stiffness: 400,
|
||||
}}
|
||||
rotationInterval={2000}
|
||||
/>
|
||||
</h1>
|
||||
<p className="text-lg font-semibold text-center pb-4">
|
||||
¿ Buscas un seguro cercano y con alguien de confianza ?
|
||||
</p>
|
||||
<div className="flex justify-center items-center gap-2">
|
||||
<Button
|
||||
className="font-bold"
|
||||
endContent={
|
||||
<span className="iconify cib--whatsapp size-5 text-green-700" />
|
||||
}
|
||||
as={Link}
|
||||
to={waUrl}
|
||||
>
|
||||
Whatsapp
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/formulario",
|
||||
viewTransition: true,
|
||||
})
|
||||
}
|
||||
endContent={
|
||||
<span className="iconify size-5 solar--verified-check-broken" />
|
||||
}
|
||||
>
|
||||
Calcular precio
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mt-5">
|
||||
{insuranceCard.map((v) => (
|
||||
<Card className="px-0" key={v.name}>
|
||||
<CardHeader className="justify-center">
|
||||
<h1 className="text-xl">{v?.name}</h1>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="flex justify-center">
|
||||
<span className={v?.icon} />
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="justify-center">
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: v.link,
|
||||
viewTransition: true,
|
||||
})
|
||||
}
|
||||
color="secondary"
|
||||
>
|
||||
Ver más
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full py-8">
|
||||
<div className="flex justify-center flex-col items-center gap-4">
|
||||
<Card className="max-w-5xl overflow-y-hidden" shadow="none">
|
||||
<CardBody>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div className="flex items-center justify-center min-h-[380px]">
|
||||
<Image
|
||||
width={400}
|
||||
height={380}
|
||||
src="/conocenos.webp"
|
||||
alt="victoria seguros-mayores"
|
||||
fallbackSrc="/conocenos.webp"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold mb-3 flex gap-2 items-center">
|
||||
<p className="text-2xl font-semibold">
|
||||
¿Por qué nos contratan?
|
||||
</p>
|
||||
</h1>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
En nuestra aseguradora, apostamos por un trato
|
||||
verdaderamente personalizado. Contamos con agentes
|
||||
asignados y cercanos que te acompañan con experiencia y
|
||||
compromiso. Además, si lo necesitas, también estamos
|
||||
disponibles en nuestras oficinas físicas en Alicante. Una
|
||||
llamada es suficiente para conocernos.
|
||||
</p>
|
||||
<p className="font-semibold">
|
||||
Ofrecemos una atención a medida, sin intermediarios.
|
||||
Creemos que un trato personal es la única manera de
|
||||
comprender a la perfección las necesidades de cada
|
||||
cliente.
|
||||
</p>
|
||||
<p>Número de mediadora: <a href="https://dgsfp.mineco.gob.es/" className="cursor-pointer">C015723313192R</a></p>
|
||||
<Chip className="flex items-center gap-2 text-md" variant="light">
|
||||
Más de{" "}
|
||||
<span className="text-secondary-500 font-black text-lg">
|
||||
<CountUp
|
||||
from={0}
|
||||
to={8000}
|
||||
separator=","
|
||||
direction="up"
|
||||
duration={1}
|
||||
className="count-up-text"
|
||||
/>{" "}
|
||||
</span>
|
||||
clientes confian en nosotras
|
||||
</Chip>
|
||||
<Chip className="flex items-center gap-2 text-md" variant="light">
|
||||
Más de{" "}
|
||||
<span className="text-secondary-500 font-black text-lg">
|
||||
<CountUp
|
||||
from={0}
|
||||
to={400}
|
||||
separator=","
|
||||
direction="up"
|
||||
duration={1}
|
||||
className="count-up-text"
|
||||
/>{" "}
|
||||
</span>
|
||||
seguros cerrados al mes
|
||||
</Chip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<div className="flex w-full justify-end">
|
||||
<Button
|
||||
className="max-w-md bg-primary-300 font-semibold"
|
||||
endContent={
|
||||
<span className="iconify size-5 solar--alt-arrow-right-linear" />
|
||||
}
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/formulario",
|
||||
viewTransition: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
Quiero una llamada
|
||||
</Button>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card className="max-w-5xl" shadow="none">
|
||||
<CardHeader></CardHeader>
|
||||
<CardBody>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div className="flex items-center justify-center overflow-hidden">
|
||||
<div className="w-full min-h-[280px] relative">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 50, position: "absolute" }}
|
||||
whileInView={{ opacity: 1, y: 0, position: "static" }}
|
||||
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
className="w-full"
|
||||
>
|
||||
<Image
|
||||
width={400}
|
||||
height={280}
|
||||
className="overflow-y-hidden"
|
||||
src="https://images.unsplash.com/photo-1554331292-735256644d5f?q=80&w=2076&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="victoria seguros-mayores"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold mb-4">
|
||||
Pago mensual hasta 80 años
|
||||
</h1>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
Entendemos que cada etapa de la vida presenta necesidades
|
||||
únicas. Por ello, en Victoria Seguros, ofrecemos un plan
|
||||
de pago mensual especialmente diseñado para personas
|
||||
mayores de 60 años, con posibilidad de contratación
|
||||
<span className="font-bold">¡hasta los 80!</span>
|
||||
</p>
|
||||
<p>
|
||||
Este plan te permite disfrutar de la cobertura esencial
|
||||
que necesitas sin sorpresas ni cargas económicas,
|
||||
manteniendo tus finanzas equilibradas. Además, contarás
|
||||
con el respaldo y la confianza que caracterizan a nuestra
|
||||
aseguradora.
|
||||
</p>
|
||||
<p>
|
||||
Tu tranquilidad y seguridad son nuestra prioridad, sin
|
||||
importar la edad.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center my-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 max-w-6xl w-full">
|
||||
{indexCards?.map((pc) => (
|
||||
<Card className="border-none" shadow="none" key={pc.title}>
|
||||
<CardBody className="justify-center items-center flex flex-row md:flex-col gap-4">
|
||||
<span
|
||||
className={`${pc.icon} size-5 md:size-12 text-secondary/50 justify-start`}
|
||||
/>
|
||||
<h3 className="md:text-lg font-semibold text-sm">
|
||||
{pc.title}
|
||||
</h3>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="flex flex-col gap-4">
|
||||
<div className="flex w-full justify-end">
|
||||
<Button
|
||||
className="max-w-md bg-primary-300 font-semibold"
|
||||
endContent={
|
||||
<span className="iconify size-5 solar--alt-arrow-right-linear" />
|
||||
}
|
||||
onPress={() =>
|
||||
navigate({
|
||||
to: "/formulario",
|
||||
viewTransition: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
Quiero está oferta
|
||||
</Button>
|
||||
</div>
|
||||
<div className="max-w-[100%]"></div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<Outlet />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
196
src/routes/seguros/decesos.tsx
Normal file
196
src/routes/seguros/decesos.tsx
Normal file
@ -0,0 +1,196 @@
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import { Image } from "@heroui/image";
|
||||
import { Button } from "@heroui/button";
|
||||
import {
|
||||
dataAccordion,
|
||||
dataChipDecesos,
|
||||
petsCards,
|
||||
servicesAccordion,
|
||||
} from "~/content/decesos";
|
||||
import { seo } from "~/utils/seo";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
|
||||
export const Route = createFileRoute("/seguros/decesos")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Decesos",
|
||||
description: `Seguros de decesos en alicante`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de Decesos</h1>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="grid w-full md:grid-cols-3">
|
||||
<div className="md:col-span-2">
|
||||
<p className="">
|
||||
Protege a los tuyos y disfruta de servicios exclusivos con
|
||||
nuestro Plan de Asistencia Familiar, mucho más que un seguro de
|
||||
decesos.
|
||||
<span className="font-semibold">
|
||||
Ampliamos coberturas: mascotas, asesoría legal, asistencia
|
||||
dental y médica desde solo 2€/mes.
|
||||
</span>
|
||||
</p>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChipDecesos?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2 justify-center mb-5">
|
||||
<ButtonCall secure="decesos" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2 w-full justify-center flex items-center">
|
||||
<Card className="border-none" radius="lg" shadow="none" fullWidth>
|
||||
<CardBody>
|
||||
<Image
|
||||
alt="Manos en busca de ayuda"
|
||||
className="object-cover"
|
||||
src="https://images.unsplash.com/photo-1541976844346-f18aeac57b06?q=80&w=1935&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
height={250}
|
||||
width={600}
|
||||
/>
|
||||
</CardBody>
|
||||
|
||||
<CardFooter className="justify-between bg-transparent overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] ml-1 z-10">
|
||||
<Button
|
||||
as={Link}
|
||||
variant="faded"
|
||||
color="success"
|
||||
to="/"
|
||||
viewTransition
|
||||
endContent={
|
||||
<span className="iconify cib--whatsapp size-5 " />
|
||||
}
|
||||
>
|
||||
Contactanos
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-4">
|
||||
<Tabs aria-label="Options" className="flex justify-center">
|
||||
<Tab title="Coberturas">
|
||||
<Accordion defaultExpandedKeys={["sepelio"]}>
|
||||
{dataAccordion?.map((ac) => (
|
||||
<AccordionItem
|
||||
key={ac.key}
|
||||
aria-label={ac.title}
|
||||
title={ac.title}
|
||||
startContent={<span className={ac.icon} />}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<p className="mx-4">{ac?.descripcion}</p>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</Tab>
|
||||
<Tab title="Servicios">
|
||||
<Accordion defaultExpandedKeys={["acceso-especialistas"]}>
|
||||
{servicesAccordion?.map((ac) => (
|
||||
<AccordionItem
|
||||
key={ac.key}
|
||||
aria-label={ac.title}
|
||||
title={ac.title}
|
||||
startContent={<span className={ac.icon} />}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<p className="">{ac?.descripcion}</p>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="block">
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
Ahora con tu seguro de decesos también puedes incluir la
|
||||
Responsabilidad Civil para perros
|
||||
</h1>
|
||||
<p className="text-">
|
||||
Con la Asistencia a Mascotas también podrás contratar la
|
||||
cobertura de Responsabilidad Civil para perros peligrosos y no
|
||||
peligrosos, obligatorio con la nueva Ley de Bienestar Animal.
|
||||
</p>
|
||||
<div className="flex justify-center my-2">
|
||||
<Button className="max-w-md" size="lg">
|
||||
Solicitar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
src="https://images.unsplash.com/photo-1519052537078-e6302a4968d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Tu mascota es uno más de la familia
|
||||
</h1>
|
||||
<div className="grid md:grid-cols-4 gap-4">
|
||||
{petsCards?.map((pc) => (
|
||||
<Card className="border-none" shadow="none">
|
||||
<CardHeader className="items-center">
|
||||
<h3 className="text-lg font-semibold">{pc.title}</h3>
|
||||
<span
|
||||
className={`${pc.icon} size-20 absolute top-0 right-5 text-purple-500/15`}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="text-sm">{pc.description}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
244
src/routes/seguros/hogar.tsx
Normal file
244
src/routes/seguros/hogar.tsx
Normal file
@ -0,0 +1,244 @@
|
||||
import React from "react";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableColumn,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableCell,
|
||||
} from "@heroui/table";
|
||||
import { homeCards, indexCards } from "~/content/hogar";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import { dataChipHome, accordionfrequentlyaskedquestions } from "~/content/hogar";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { seo } from "~/utils/seo";
|
||||
type CellValue = React.ReactNode | string;
|
||||
|
||||
interface RowData {
|
||||
key: string;
|
||||
label: string;
|
||||
values: CellValue[];
|
||||
}
|
||||
|
||||
|
||||
const iconCheck = (
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500" />
|
||||
);
|
||||
const iconCross = (
|
||||
<span className="iconify solar--shield-cross-bold-duotone size-6 text-red-500" />
|
||||
);
|
||||
|
||||
|
||||
const columns = [
|
||||
{ key: "a", label: "" },
|
||||
{ key: "basico", label: "Básico" },
|
||||
{ key: "estandar", label: "Estándar" },
|
||||
{ key: "premium", label: "Premium" },
|
||||
];
|
||||
|
||||
const rows: RowData[] = [
|
||||
{ key: "incendio", label: "Incendio", values: [iconCheck, iconCheck, iconCheck] },
|
||||
{ key: "servicio-asistencia", label: "Servicio de asistencia", values: [iconCheck, iconCheck, iconCheck] },
|
||||
{ key: "responsabilidad-civil", label: "Responsabilidad civil", values: ["Solo incendio", iconCheck, iconCheck] },
|
||||
{ key: "alimento-refrigerados", label: "Alimentos refrigerados", values: [iconCross, iconCheck, iconCheck] },
|
||||
{ key: "daños-por-agua", label: "Daños por agua", values: [iconCross, iconCheck, iconCheck] },
|
||||
{ key: "roturas", label: "Roturas", values: [iconCross, iconCheck, iconCheck] },
|
||||
{ key: "robo-y-vandalismo", label: "Robo y vandalismo", values: [iconCross, iconCheck, iconCheck] },
|
||||
{ key: "coberturas-consecuenciales", label: "Coberturas consecuenciales", values: [iconCross, iconCheck, iconCheck] },
|
||||
{ key: "defensa-jurídica", label: "Defensa jurídica", values: [iconCross, iconCheck, iconCheck] },
|
||||
{ key: "robo-integral", label: "Robo Integral", values: [iconCross, iconCross, iconCheck] },
|
||||
{ key: "daños-eléctricos", label: "Daños eléctricos", values: [iconCross, iconCross, iconCheck] },
|
||||
{ key: "bricohogar", label: "Bricohogar", values: [iconCross, iconCross, iconCheck] },
|
||||
];
|
||||
|
||||
const optionalRows: RowData[] = [
|
||||
{ key: "accidentes", label: "Accidentes", values: [iconCross, "Opcional", "Opcional"] },
|
||||
{ key: "mascotas", label: "Mascotas", values: [iconCross, "Opcional", "Opcional"] },
|
||||
{ key: "vehículos-y-motos-en-garaje", label: "Vehículos y motos en garaje", values: [iconCross, "Opcional", "Opcional"] },
|
||||
{ key: "robo-objetos-valor-especial", label: "Robo de objetos de valor especial", values: [iconCross, iconCross, "Opcional"] },
|
||||
];
|
||||
|
||||
function renderRows(rowsData: RowData[]) {
|
||||
return rowsData.map(({ key, label, values }) => (
|
||||
<TableRow key={key}>
|
||||
<TableCell className="font-semibold">{label}</TableCell>
|
||||
{values.map((val, i) => (
|
||||
<TableCell key={i}>{val}</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
));
|
||||
}
|
||||
|
||||
// Tabla responsive para móviles (simple versión vertical)
|
||||
function MobileTable({ rowsData }: { rowsData: RowData[] }) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{rowsData.map(({ key, label, values }) => (
|
||||
<div
|
||||
key={key}
|
||||
role="table"
|
||||
aria-label={label}
|
||||
className="border border-gray-300 rounded-lg p-4 shadow-sm bg-white"
|
||||
>
|
||||
<div className="font-semibold mb-4 text-gray-800 text-lg border-b border-gray-200 pb-2">
|
||||
{label}
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 text-center text-sm">
|
||||
{columns.slice(1).map((col, i) => (
|
||||
<div
|
||||
key={col.key}
|
||||
className="flex flex-col items-center bg-gray-50 rounded-md p-3 hover:bg-gray-100 transition"
|
||||
>
|
||||
<span className="font-semibold mb-2 text-gray-700">{col.label}</span>
|
||||
<span className="text-gray-900">{values[i]}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RouteComponent() {
|
||||
// Detectamos ancho de pantalla para mostrar tabla normal o mobile
|
||||
// Aquí por simplicidad usamos un hook, en caso real usaría react-responsive o css para ocultar/mostrar.
|
||||
const [isMobile, setIsMobile] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const onResize = () => setIsMobile(window.innerWidth < 640);
|
||||
onResize();
|
||||
window.addEventListener("resize", onResize);
|
||||
return () => window.removeEventListener("resize", onResize);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de Hogar</h1>
|
||||
<div className="flex flex-wrap gap-2 my-4">
|
||||
{dataChipHome?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
<p className="mx-4">
|
||||
El Seguro de Hogar te permite asegurar lo que realmente necesitas.{" "}
|
||||
<span className="font-semibold ">
|
||||
Porque no todas las viviendas son iguales, ni todas las familias
|
||||
tienen las mismas necesidades.
|
||||
</span>
|
||||
</p>
|
||||
<div className="flex gap-2 justify-center mb-5">
|
||||
<ButtonCall secure="hogar" />
|
||||
</div>
|
||||
|
||||
<div className="mx-4">
|
||||
<Tabs aria-label="Options" className="flex justify-center">
|
||||
<Tab title="Coberturas">
|
||||
{isMobile ? (
|
||||
<MobileTable rowsData={rows} />
|
||||
) : (
|
||||
<Table aria-label="Tabla de coberturas">
|
||||
<TableHeader>
|
||||
{columns.map((column) => (
|
||||
<TableColumn key={column.key}>{column.label}</TableColumn>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>{renderRows(rows)}</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Coberturas Opcionales">
|
||||
{isMobile ? (
|
||||
<MobileTable rowsData={optionalRows} />
|
||||
) : (
|
||||
<Table aria-label="Tabla de coberturas opcionales">
|
||||
<TableHeader>
|
||||
{columns.map((column) => (
|
||||
<TableColumn key={column.key}>{column.label}</TableColumn>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>{renderRows(optionalRows)}</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</CardBody>
|
||||
|
||||
<CardFooter className="block">
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Te prestamos servicio las 24 horas del día
|
||||
</h1>
|
||||
<div className="grid md:grid-cols-4 gap-4">
|
||||
{homeCards?.map((pc) => (
|
||||
<Card key={pc.title} className="border-none" shadow="none">
|
||||
<CardHeader className="items-center relative">
|
||||
<h3 className="text-lg font-semibold">{pc.title}</h3>
|
||||
<span
|
||||
className={`${pc.icon} size-20 absolute top-0 right-5 text-purple-500/15`}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="text-sm">{pc.description}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Preguntas frecuentes
|
||||
</h1>
|
||||
<Accordion>
|
||||
{accordionfrequentlyaskedquestions?.map((pf) => (
|
||||
<AccordionItem
|
||||
key={pf.title}
|
||||
title={pf.title}
|
||||
classNames={{ title: "font-semibold" }}
|
||||
>
|
||||
{pf.description}
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const Route = createFileRoute("/seguros/hogar")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ charSet: "utf-8" },
|
||||
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
95
src/routes/seguros/mascotas/caballos.tsx
Normal file
95
src/routes/seguros/mascotas/caballos.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import { dataChipCaballos, dataAccordionCaballos } from "~/content/perros";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { seo } from "~/utils/seo";
|
||||
|
||||
export const Route = createFileRoute("/seguros/mascotas/caballos")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de Caballos</h1>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChipCaballos?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="font-semibold text-sm mx-4">
|
||||
Asegura tu tranquilidad con el seguro de Responsabilidad Civil para
|
||||
perros, que te proporcionará protección en caso de daños a terceros.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2n justify-center mb-5">
|
||||
<ButtonCall secure="caballos"/>
|
||||
</div>
|
||||
|
||||
<div className="mx-4">
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
¿Por qué contratar el Seguro de Caballos de Victoria Seguros?
|
||||
</h1>
|
||||
|
||||
<Tabs aria-label="Options" className="flex justify-center">
|
||||
<Tab title="Coberturas">
|
||||
<Accordion defaultExpandedKeys={["sepelio"]}>
|
||||
{dataAccordionCaballos?.map((ac) => (
|
||||
<AccordionItem
|
||||
key={ac.key}
|
||||
aria-label={ac.title}
|
||||
title={ac.title}
|
||||
startContent={<span className={ac.icon} />}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<p className="">{ac?.descripcion}</p>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="block"></CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
72
src/routes/seguros/mascotas/index.tsx
Normal file
72
src/routes/seguros/mascotas/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Button } from "@heroui/button";
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import { seo } from "~/utils/seo";
|
||||
import { Image } from "@heroui/image";
|
||||
|
||||
export const Route = createFileRoute("/seguros/mascotas/")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<div className="grid max-w-5xl md:grid-cols-2 gap-4 my-5 mx-4">
|
||||
<Card className="border-none" radius="lg">
|
||||
<Image
|
||||
alt="Woman listing to music"
|
||||
className="object-cover"
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1588943211346-0908a1fb0b01?q=80&w=1935&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
width={300}
|
||||
/>
|
||||
<CardFooter className="justify-between bg-white border-white/20 border-1 overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
||||
<p className="font-semibold">Seguros de Perros</p>
|
||||
<Button as={Link} color="secondary" to="perros" viewTransition>
|
||||
Ver más
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card className="border-none" radius="lg">
|
||||
<Image
|
||||
alt="Woman listing to music"
|
||||
className="object-cover"
|
||||
height={300}
|
||||
src="https://images.unsplash.com/uploads/14136148007774dc82563/ce92d553?q=80&w=1946&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
width={300}
|
||||
/>
|
||||
<CardFooter className="justify-between bg-white border-white/20 border-1 overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
||||
<p className="font-semibold">Seguro de Caballos</p>
|
||||
<Button as={Link} color="secondary" to="caballos" viewTransition>
|
||||
Ver más
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
203
src/routes/seguros/mascotas/perros.tsx
Normal file
203
src/routes/seguros/mascotas/perros.tsx
Normal file
@ -0,0 +1,203 @@
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import { Image } from "@heroui/image";
|
||||
import { Button } from "@heroui/button";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import { dataChip, dataAccordion, petsCards } from "~/content/perros";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { seo } from "~/utils/seo";
|
||||
|
||||
export const Route = createFileRoute("/seguros/mascotas/perros")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de Perros</h1>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChip?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="font-semibold text-sm mx-4">
|
||||
Asegura tu tranquilidad con el seguro de Responsabilidad Civil para
|
||||
perros, que te proporcionará protección en caso de daños a terceros.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2 mb-5 justify-center">
|
||||
<ButtonCall secure="perros"/>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 gap-2 mb-10 mt-5">
|
||||
<Card>
|
||||
<CardBody>
|
||||
{" "}
|
||||
<div className="flex justify-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={300}
|
||||
height={200}
|
||||
src="https://images.unsplash.com/photo-1561037404-61cd46aa615b?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
className="max-w-md self-center"
|
||||
/>
|
||||
</div>
|
||||
<p className="font-bold text-sm absolute z-20 h-24 w-24 rounded-full bg-secondary text-default-50 p-1 flex text-center items-center translate-x-5">
|
||||
Desde 52€/año
|
||||
</p>
|
||||
<p className="font-semibold text-xl text-center">
|
||||
Seguro de perro
|
||||
</p>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<p className="text-sm">
|
||||
El seguro de perro obligatorio para razas no peligrosas:
|
||||
Labrador, Golden Retriever, Bulldog Francés, Bichón Maltés,
|
||||
Pastor Alemán, Beagle, Setter Inglés, Caniche, Yorkshire,
|
||||
Schnauzer, Chihuahua..., entre otras.
|
||||
</p>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardBody className="">
|
||||
<div className="flex justify-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={300}
|
||||
height={200}
|
||||
src="https://images.unsplash.com/photo-1600369671738-fa3a43efeced?q=80&w=1964&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
className="max-w-md"
|
||||
/>
|
||||
</div>
|
||||
<p className="font-bold text-sm absolute z-20 h-24 w-24 rounded-full bg-secondary text-default-50 p-1 flex text-center items-center translate-x-5">
|
||||
Desde 105€/año
|
||||
</p>
|
||||
<p className="font-semibold text-xl text-center">
|
||||
Seguro de perro peligroso
|
||||
</p>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<p className="text-sm">
|
||||
El seguro de perro obligatorio para razas peligrosas: Pit Bull
|
||||
Terrier, Rottweiler, Dogo Argentino, American Staffordshire,
|
||||
Fila Brasileño, Tosa Inu, Akira Inu, Presa Canario,
|
||||
Bullmastiff..., entre otras.
|
||||
</p>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Coberturas del seguro de Responsabilidad Civil para perros
|
||||
</h1>
|
||||
|
||||
<Tabs aria-label="Options" className="flex justify-center">
|
||||
<Tab title="Coberturas">
|
||||
<Accordion defaultExpandedKeys={["sepelio"]}>
|
||||
{dataAccordion?.map((ac) => (
|
||||
<AccordionItem
|
||||
key={ac.key}
|
||||
aria-label={ac.title}
|
||||
title={ac.title}
|
||||
startContent={<span className={ac.icon} />}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<p className="">{ac?.descripcion}</p>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="block">
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Ventajas del seguro de Responsabilidad Civil para perros
|
||||
</h1>
|
||||
<div className="grid md:grid-cols-4 gap-4">
|
||||
{petsCards?.map((pc) => (
|
||||
<Card className="border-none" shadow="none">
|
||||
<CardHeader className="items-center">
|
||||
<h3 className="text-lg font-semibold">{pc.title}</h3>
|
||||
<span
|
||||
className={`${pc.icon} size-20 absolute top-0 right-5 text-purple-500/15`}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="text-sm">{pc.description}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
Ahora con tu seguro de decesos también puedes incluir la
|
||||
Responsabilidad Civil para perros
|
||||
</h1>
|
||||
<p className="text-">
|
||||
Con la Asistencia a Mascotas también podrás contratar la
|
||||
cobertura de Responsabilidad Civil para perros peligrosos y no
|
||||
peligrosos, obligatorio con la nueva Ley de Bienestar Animal.
|
||||
</p>
|
||||
<div className="flex justify-center my-2">
|
||||
<Button className="max-w-md" size="lg">
|
||||
Solicitar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
src="https://images.unsplash.com/photo-1519052537078-e6302a4968d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
366
src/routes/seguros/salud.tsx
Normal file
366
src/routes/seguros/salud.tsx
Normal file
@ -0,0 +1,366 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import { Image } from "@heroui/image";
|
||||
import { Button } from "@heroui/button";
|
||||
import { servicesAccordion } from "~/content/decesos";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
import { dataCardDemo, dataCardHealth, dataChipHealth } from "~/content/salud";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { seo } from "~/utils/seo";
|
||||
|
||||
export const Route = createFileRoute("/seguros/salud")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Tú aseguradora de confianza",
|
||||
description: `La aseguradora n1 de Alicante y Costa del Sol `,
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de Salud</h1>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChipHealth?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody className="flex flex-col">
|
||||
<div className="mb-5">
|
||||
<p className="font-semibold mx-4">
|
||||
Hay cosas que cuando llegan, te cambian la vida, como el seguro de
|
||||
Salud Integral de Helvetia. Un seguro de Salud COMPLETO y SIN COPAGOS
|
||||
hasta el 11º uso.
|
||||
</p>
|
||||
<div className="flex gap-2 justify-center mb-5">
|
||||
<ButtonCall secure="salud" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-4 gap-4 my-4">
|
||||
{dataCardDemo?.map((pc) => (
|
||||
<Card className="border-none" shadow="none">
|
||||
<CardHeader className="items-center">
|
||||
<h3 className="text-lg font-semibold">{pc.title}</h3>
|
||||
<span
|
||||
className={`${pc.icon} size-20 absolute top-0 right-5 text-purple-500/15`}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="text-sm">{pc.description}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<div className="mx-4">
|
||||
<Tabs aria-label="Options" className="flex justify-center">
|
||||
<Tab title="Coberturas">
|
||||
<Accordion defaultExpandedKeys={["cp"]}>
|
||||
<AccordionItem
|
||||
key={"cp"}
|
||||
aria-label={"Coberturas principales"}
|
||||
title={"Coberturas principales"}
|
||||
startContent={
|
||||
<span
|
||||
className={"iconify solar--jar-of-pills-2-line-duotone"}
|
||||
/>
|
||||
}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<ul className="">
|
||||
<li>- Medicina general</li>
|
||||
<li>- Urgencias hospitalarias </li>
|
||||
<li>- Urgencias domiciliarias</li>
|
||||
<li>- Especialidades médicas</li>
|
||||
<li>- Asistencia médica 24 horas</li>
|
||||
<li>- Pruebas diagnósticas</li>
|
||||
<li>- Asistencia médica en el extranjero</li>
|
||||
<li>- Hospitalización</li>
|
||||
<li>- Segunda opinión médica</li>
|
||||
<li>- Complemento bucodental</li>
|
||||
<li>- Prótesis internas</li>
|
||||
</ul>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key={"te"}
|
||||
aria-label={"Tratamientos especiales"}
|
||||
title={"Tratamientos especiales"}
|
||||
startContent={
|
||||
<span
|
||||
className={"iconify solar--dropper-2-line-duotone"}
|
||||
/>
|
||||
}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<ul className="">
|
||||
<li>- Psicología </li>
|
||||
<li>- Podología </li>
|
||||
<li>
|
||||
- Otros tratamientos especiales (oxigenoterapia,
|
||||
laserterapia, litroticia extracorpórea renal, diálisis,
|
||||
quimioterapia, radioterapia...)
|
||||
</li>
|
||||
<li>
|
||||
- Dermatoscopia digital - detección precoz del melanoma
|
||||
</li>
|
||||
<li>- Dianas terapéuticas - tratamiento oncológico</li>
|
||||
<li>
|
||||
- Cirugía robotizada en intervenciones de cáncer de
|
||||
próstata con el sistema Da Vinci
|
||||
</li>
|
||||
<li>- Cirugía de la mama sana</li>
|
||||
<li>- Cirugía esófago de Barret</li>
|
||||
<li>- Rehabilitación cardiaca tras infarto</li>
|
||||
<li>- Estudio biomecánico de la pisada</li>
|
||||
<li>- Rehabilitación del suelo pélvico </li>
|
||||
<li>
|
||||
- Rehabilitación vestibular para patología de oído
|
||||
interno
|
||||
</li>
|
||||
</ul>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key={"ep"}
|
||||
aria-label={"Embarazo y planificación familiar"}
|
||||
title={"Embarazo y planificación familiar"}
|
||||
startContent={
|
||||
<span className={"iconify solar--dna-line-duotone"} />
|
||||
}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<ul className="">
|
||||
<li>- Diagnóstico y tratamiento de la infertilidad</li>{" "}
|
||||
<li>- Programa de preparación al parto</li>{" "}
|
||||
<li>- Test de cribado prenatal no invasivo </li>
|
||||
<li>- Cuidados posparto</li>{" "}
|
||||
<li>- Planificación familiar</li>
|
||||
</ul>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key={"tm"}
|
||||
aria-label={"Telemedicina"}
|
||||
title={"Telemedicina"}
|
||||
startContent={
|
||||
<span
|
||||
className={"iconify solar--videocamera-line-duotone"}
|
||||
/>
|
||||
}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<ul className="">
|
||||
<li>- Llamadas, videollamadas, chat médico online </li>
|
||||
<li>- Sin copagos y sin límite de consultas </li>
|
||||
<li>- Medicina general y 29 especialidades </li>
|
||||
<li>- Prescripciones para pruebas y medicamentos</li>
|
||||
<li>
|
||||
- Selfie health: medidor de indicadores de salud desde
|
||||
tu móvil (tensión, frecuencia cardiaca, estrés...)
|
||||
</li>
|
||||
</ul>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</Tab>
|
||||
<Tab title="Servicios">
|
||||
<Accordion defaultExpandedKeys={["acceso-especialistas"]}>
|
||||
{servicesAccordion?.map((ac) => (
|
||||
<AccordionItem
|
||||
key={ac.key}
|
||||
aria-label={ac.title}
|
||||
title={ac.title}
|
||||
startContent={<span className={ac.icon} />}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<p className="">{ac?.descripcion}</p>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="block">
|
||||
<Card shadow="none" className="my-8 gap-2 border-none">
|
||||
<CardBody className="gap-6 grid ">
|
||||
<div>
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Precios y descuentos exclusivos
|
||||
</h1>
|
||||
<h2 className="text-lg mb-6 font-semibold text-gray-700 text-center">
|
||||
Te ayudamos con tus gastos médicos mediante diferentes
|
||||
reembolsos.
|
||||
</h2>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-4 gap-4">
|
||||
{dataCardHealth?.map((pc) => (
|
||||
<Card className="border-none" shadow="none">
|
||||
<CardHeader className="items-center">
|
||||
<h3 className="text-lg font-semibold">{pc.title}</h3>
|
||||
<span
|
||||
className={`${pc.icon} size-20 absolute top-0 right-5 text-purple-500/15`}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="text-sm">{pc.description}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
Plataforma Mi Bienestar Emocional
|
||||
</h1>
|
||||
<p className="text-">
|
||||
Mejora el cuidado de tu mente y tus emociones con la
|
||||
Plataforma Mi Bienestar Emocional. Herramienta online, con
|
||||
sesiones en directo, guiada por profesionales en el campo de
|
||||
la psicología. Con ella aprenderás de forma rápida y sencilla,
|
||||
diferentes rutinas y hábitos a incorporar en tu día a día para
|
||||
mejorar el cuidado de tu mente y emociones.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1482100199117-a4a38a64e7e3?q=80&w=2069&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1554244933-d876deb6b2ff?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
Plataforma Caser Más Beneficios
|
||||
</h1>
|
||||
<p className="text-">
|
||||
Encontrarás servicios orientados a la prevención y el
|
||||
bienestar de tu salud, y también, al mantenimiento del hogar.
|
||||
¿El objetivo? Ayudarte en tu día a día con servicios
|
||||
complementarios al seguro. Todos nuestros servicios se llevan
|
||||
a cabo en clínicas de prestigio y las mejores empresas en cada
|
||||
sector, para que siempre recibas una atención personalizada y
|
||||
de calidad
|
||||
</p>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
Descárgate nuestra app Helvetia Salud
|
||||
</h1>
|
||||
<p className="text-">
|
||||
La app con la que gestionar todo lo relacionado con tu seguro
|
||||
de salud. Porque hacer uso de tu seguro nunca fue tan simple y
|
||||
rápido. Desde cualquier dispositivo y estés donde estés,
|
||||
innovación digital para facilitar y mejorar tu día a día.
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Consulta el Cuadro Médico para encontrar al especialista más
|
||||
cercano.{" "}
|
||||
</li>
|
||||
<li>
|
||||
Solicita y consulta tus autorizaciones médicas y reembolsos.
|
||||
</li>
|
||||
<li>
|
||||
Utiliza tu tarjeta digital para identificarte en los centros
|
||||
médicos.{" "}
|
||||
</li>
|
||||
<li>
|
||||
Otros servicios como: Centro médico telemedicina, Caser
|
||||
+Beneficios, promociones...{" "}
|
||||
</li>
|
||||
</ol>
|
||||
<Button
|
||||
as={Link}
|
||||
to="https://apps.apple.com/us/app/victoria seguros-salud/id6469604635"
|
||||
startContent={
|
||||
<span className="iconify size-6 cib--app-store" />
|
||||
}
|
||||
color="secondary"
|
||||
>
|
||||
AppStore - Apple
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
to="https://play.google.com/store/apps/details?id=com.caser.victoria seguros.salud"
|
||||
startContent={
|
||||
<span className="iconify size-6 cib--google-play" />
|
||||
}
|
||||
color="secondary"
|
||||
>
|
||||
PlayStore - Android
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1522125670776-3c7abb882bc2?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
84
src/routes/seguros/vehiculos/coche.tsx
Normal file
84
src/routes/seguros/vehiculos/coche.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import { Image } from "@heroui/image";
|
||||
import { Button } from "@heroui/button";
|
||||
import {
|
||||
dataAccordion,
|
||||
dataChipDecesos,
|
||||
petsCards,
|
||||
servicesAccordion,
|
||||
} from "~/content/decesos";
|
||||
import { seo } from "~/utils/seo";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { dataChipCoche } from "~/content/coche";
|
||||
|
||||
export const Route = createFileRoute("/seguros/vehiculos/coche")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Decesos",
|
||||
description: `Seguros de decesos en alicante`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, vehículos, coche, coches, motos, 4x4, camiones, alicante, seguros, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de coches</h1>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="grid w-full md:grid-cols-3">
|
||||
<div className="md:col-span-2">
|
||||
<p className="">
|
||||
Existen muchas opciones de seguros de auto, pero pocas son tan
|
||||
completas como el que te ofrece Victoria Seguros. Nuestra póliza
|
||||
se adapta a tus necesidades y a las de tu vehículo, brindándote
|
||||
la protección que realmente importa.
|
||||
<span className="font-semibold">
|
||||
{" "}
|
||||
Descubre todas nuestras coberturas y elige la que mejor se
|
||||
adapte a tus necesidades.
|
||||
</span>
|
||||
</p>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChipCoche?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2 justify-center mb-5">
|
||||
<ButtonCall secure="coche" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
49
src/routes/seguros/vehiculos/index.tsx
Normal file
49
src/routes/seguros/vehiculos/index.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Button } from "@heroui/button";
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Image } from "@heroui/image";
|
||||
|
||||
export const Route = createFileRoute("/seguros/vehiculos/")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div className="grid max-w-5xl sm:grid-cols-2 gap-4 my-5 mx-4">
|
||||
<Card className="border-none" radius="lg">
|
||||
<Image
|
||||
alt="Woman listing to music"
|
||||
className="object-cover"
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1611448746128-7c39e03b71e4?q=80&w=1949&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
width={300}
|
||||
/>
|
||||
<CardFooter className="justify-between bg-white border-white/20 border-1 overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
||||
<p className="font-semibold">Seguros de Coche</p>
|
||||
<Button as={Link} color="secondary" to="coche" viewTransition>
|
||||
Ver más
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card className="border-none" radius="lg">
|
||||
<Image
|
||||
alt="Woman listing to music"
|
||||
className="object-cover"
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1558981806-ec527fa84c39?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
width={300}
|
||||
/>
|
||||
<CardFooter className="justify-between bg-white border-white/20 border-1 overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-[calc(100%_-_8px)] shadow-small ml-1 z-10">
|
||||
<p className="font-semibold">Seguro de Motos</p>
|
||||
<Button as={Link} color="secondary" to="moto" viewTransition>
|
||||
Ver más
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
77
src/routes/seguros/vehiculos/moto.tsx
Normal file
77
src/routes/seguros/vehiculos/moto.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { Card, CardBody, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { seo } from "~/utils/seo";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { dataChipMoto } from "~/content/coche";
|
||||
|
||||
export const Route = createFileRoute("/seguros/vehiculos/moto")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Decesos",
|
||||
description: `Seguros de decesos en alicante`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, vehículos, coche, coches, motos, 4x4, camiones, alicante, seguros, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de motos</h1>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="grid w-full md:grid-cols-3">
|
||||
<div className="md:col-span-2">
|
||||
<p className="">
|
||||
Conduce tu motocicleta o ciclomotor con total confianza gracias
|
||||
al Seguro de Moto de Victoria Seguros: una cobertura a medida
|
||||
para todos los apasionados de las dos ruedas.
|
||||
<span className="font-semibold">
|
||||
{" "}
|
||||
La seguridad y la economía pueden ir de la mano. Si tu
|
||||
motocicleta tiene algunos años o la usas con poca frecuencia,
|
||||
un seguro a terceros es ideal. ¿Buscas algo más? El seguro a
|
||||
terceros ampliado incluye cobertura contra robo e incendio. Y
|
||||
si tienes una moto nueva y deseas la máxima protección, el
|
||||
seguro a todo riesgo con deducible es tu mejor opción.
|
||||
</span>
|
||||
</p>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChipMoto?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2 justify-center mb-5">
|
||||
<ButtonCall secure="motos" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
270
src/routes/seguros/vida.tsx
Normal file
270
src/routes/seguros/vida.tsx
Normal file
@ -0,0 +1,270 @@
|
||||
import { Card, CardBody, CardFooter, CardHeader } from "@heroui/card";
|
||||
import { Chip } from "@heroui/chip";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Tabs, Tab } from "@heroui/tabs";
|
||||
import { Image } from "@heroui/image";
|
||||
import { Button } from "@heroui/button";
|
||||
import {
|
||||
dataChipVida,
|
||||
lifesafeCards,
|
||||
servicesAccordionLife,
|
||||
} from "~/content/vida";
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableColumn,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableCell,
|
||||
// getKeyValue,
|
||||
} from "@heroui/table";
|
||||
import { Accordion, AccordionItem } from "@heroui/accordion";
|
||||
import ButtonCall from "~/components/ButtonCall";
|
||||
import { seo } from "~/utils/seo";
|
||||
import appCss from "~/styles/app.css?url";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: "a",
|
||||
label: "",
|
||||
},
|
||||
{
|
||||
key: "basico",
|
||||
label: "Plan Básico",
|
||||
},
|
||||
{
|
||||
key: "estandar",
|
||||
label: "Plan Esencial",
|
||||
},
|
||||
];
|
||||
|
||||
export const Route = createFileRoute("/seguros/vida")({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: "utf-8",
|
||||
},
|
||||
{
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
},
|
||||
...seo({
|
||||
title: "Victoria Seguros | Vida",
|
||||
description: `La aseguradora de los seguros de vida`,
|
||||
keywords:
|
||||
"seguros, seguros en Alicante, seguros en Murcia, seguros en la Costa, seguros de decesos, seguros de vida, seguros de salud, seguro de fallecimiento, cobertura funeraria, asistencia en decesos, póliza de vida, póliza de salud, seguro médico, seguro familiar, protección financiera, seguro para extranjeros, seguro de accidentes, servicio funerario, cremación, inhumación, ataúd, seguros completos, seguros económicos, aseguradora en Alicante, aseguradora en Murcia",
|
||||
}),
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
|
||||
{ rel: "icon", href: "/favicon.ico" },
|
||||
],
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<Card className="w-full max-w-5xl m-2 md:m-4 overflow-hidden">
|
||||
<CardHeader className="block">
|
||||
<h1 className="text-2xl font-bold">Seguro de Vida</h1>
|
||||
<div className="flex gap-2 flex-wrap my-4">
|
||||
{dataChipVida?.map((cp) => (
|
||||
<Chip
|
||||
className="bg-primary-100"
|
||||
key={cp.icon}
|
||||
startContent={<span className={cp.label} />}
|
||||
>
|
||||
{cp.icon}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="font-semibold mx-4">
|
||||
Garantiza el futuro de tu familia protegiéndola en caso de
|
||||
fallecimiento o invalidez, con el Seguro de Vida de Helvetia. e ofrecemos coberturas básicas de fallecimiento e
|
||||
invalidez, junto con otras más innovadoras que aumentan tu nivel de
|
||||
protección en caso de accidente o accidente de circulación. Calcula
|
||||
el precio de tu seguro de vida y protege a los que más quieres.
|
||||
</p>
|
||||
<div className="flex gap-2 justify-center mb-5">
|
||||
<ButtonCall secure="vida" />
|
||||
</div>
|
||||
|
||||
<div className="mx-4">
|
||||
<Tabs aria-label="Options" className="flex justify-center">
|
||||
<Tab title="Coberturas">
|
||||
<Table aria-label="Example table with dynamic content">
|
||||
<TableHeader>
|
||||
{columns.map((column) => (
|
||||
<TableColumn key={column.key}>{column.label}</TableColumn>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow key={"incendio"}>
|
||||
<TableCell className="font-semibold">
|
||||
Fallecimiento por cualquier causa
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key={"servicio-asistencia"}>
|
||||
<TableCell className="font-semibold">
|
||||
Capital adicional si fallecen de ambos cónyuges por
|
||||
accidente
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key={"responsabilidad-civil"}>
|
||||
<TableCell className="font-semibold">
|
||||
Helvetia Cuidados
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key={"alimento-refrigerados"}>
|
||||
<TableCell className="font-semibold">
|
||||
Invalidez Absoluta y Permanente por cualquier causa
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-cross-bold-duotone size-6 text-red-500"></span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="iconify solar--shield-check-bold-duotone size-6 text-green-500"></span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
Helvetia Cuidados
|
||||
</h1>
|
||||
<p className="text-">
|
||||
Tu app de hábitos de vida saludable con la que conseguirás
|
||||
descuentos en tu póliza mientras te cuidas más que nunca.
|
||||
</p>
|
||||
<Button
|
||||
as={Link}
|
||||
to="https://apps.apple.com/es/app/cuidados-victoria seguros/id1396448560"
|
||||
startContent={
|
||||
<span className="iconify size-6 cib--app-store" />
|
||||
}
|
||||
color="secondary"
|
||||
>
|
||||
AppStore - Apple
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
to="https://play.google.com/store/apps/details?id=com.inithealth.victoria seguros"
|
||||
startContent={
|
||||
<span className="iconify size-6 cib--google-play" />
|
||||
}
|
||||
color="secondary"
|
||||
>
|
||||
PlayStore - Android
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1522125670776-3c7abb882bc2?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</CardBody>
|
||||
<CardFooter className="block">
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Y además... Todos estos servicios de salud y bienestar{" "}
|
||||
</h1>
|
||||
<p className="text-sm">
|
||||
Victoria Seguros Cuidados te ofrece un programa de bienestar
|
||||
personalizado con consejos prácticos, contenido divertido y retos
|
||||
semanales y mensuales para ayudarte a cuidarte con facilidad y
|
||||
mejorar tu salud física y mental. Además, recibirás los siguientes
|
||||
servicios exclusivos.
|
||||
</p>
|
||||
<div className="grid md:grid-cols-4 gap-4">
|
||||
{lifesafeCards?.map((pc) => (
|
||||
<Card className="border-none" shadow="none">
|
||||
<CardHeader className="items-center">
|
||||
<h3 className="text-lg font-semibold">{pc.title}</h3>
|
||||
<span
|
||||
className={`${pc.icon} size-20 absolute top-0 right-5 text-purple-500/15`}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<p className="text-sm">{pc.description}</p>
|
||||
</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<Card shadow="sm" className="my-8 gap-2">
|
||||
<CardBody className="gap-6 grid md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<h1 className="text-xl font-semibold">
|
||||
¿No encuentras las garantías o coberturas que necesitas?{" "}
|
||||
</h1>
|
||||
<p className="text-md">
|
||||
En Victoria Seguros, ofrecemos más opciones para su seguro de vida. Puede consultarnos si ofrecemos coberturas como incapacidad temporal (para autónomos) o enfermedad grave, así como coberturas superiores a 150.000 € o cobertura profesional de alto riesgo. Contáctenos y revisaremos su caso gratuitamente.
|
||||
</p>
|
||||
<Button>Contacta con nosotros</Button>
|
||||
</div>
|
||||
<div className="grid place-content-center">
|
||||
<Image
|
||||
isZoomed
|
||||
width={400}
|
||||
height={300}
|
||||
src="https://images.unsplash.com/photo-1522125670776-3c7abb882bc2?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="prueba"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<h1 className="text-xl mb-6 font-semibold text-purple-700 text-center">
|
||||
Preguntas frequentes
|
||||
</h1>
|
||||
<Accordion defaultExpandedKeys={["acceso-especialistas"]}>
|
||||
{servicesAccordionLife?.map((ac) => (
|
||||
<AccordionItem
|
||||
key={ac.key}
|
||||
aria-label={ac.title}
|
||||
title={ac.title}
|
||||
startContent={<span className={ac.icon} />}
|
||||
classNames={{
|
||||
title: "font-semibold",
|
||||
}}
|
||||
>
|
||||
<p className="">{ac?.descripcion}</p>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
13
src/ssr.tsx
Normal file
13
src/ssr.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
/// <reference types="vinxi/types/server" />
|
||||
import {
|
||||
createStartHandler,
|
||||
defaultStreamHandler,
|
||||
} from '@tanstack/react-start/server'
|
||||
import { getRouterManifest } from '@tanstack/react-start/router-manifest'
|
||||
|
||||
import { createRouter } from './router'
|
||||
|
||||
export default createStartHandler({
|
||||
createRouter,
|
||||
getRouterManifest,
|
||||
})(defaultStreamHandler)
|
||||
26
src/styles/app.css
Normal file
26
src/styles/app.css
Normal file
@ -0,0 +1,26 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-gray-200 dark:border-gray-800;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
@apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200;
|
||||
}
|
||||
|
||||
.using-mouse * {
|
||||
outline: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.main-lay {
|
||||
background-image: url("https://images.unsplash.com/photo-1475503572774-15a45e5d60b9?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D");
|
||||
}
|
||||
33
src/utils/seo.ts
Normal file
33
src/utils/seo.ts
Normal file
@ -0,0 +1,33 @@
|
||||
export const seo = ({
|
||||
title,
|
||||
description,
|
||||
keywords,
|
||||
image,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
image?: string
|
||||
keywords?: string
|
||||
}) => {
|
||||
const tags = [
|
||||
{ title },
|
||||
{ name: 'description', content: description },
|
||||
{ name: 'keywords', content: keywords },
|
||||
{ name: 'twitter:title', content: title },
|
||||
{ name: 'twitter:description', content: description },
|
||||
{ name: 'twitter:creator', content: '@tannerlinsley' },
|
||||
{ name: 'twitter:site', content: '@tannerlinsley' },
|
||||
{ name: 'og:type', content: 'website' },
|
||||
{ name: 'og:title', content: title },
|
||||
{ name: 'og:description', content: description },
|
||||
...(image
|
||||
? [
|
||||
{ name: 'twitter:image', content: image },
|
||||
{ name: 'twitter:card', content: 'summary_large_image' },
|
||||
{ name: 'og:image', content: image },
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
||||
return tags
|
||||
}
|
||||
17
src/utils/sitemap.ts
Normal file
17
src/utils/sitemap.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { type FileRouteTypes } from "@/routeTree.gen";
|
||||
import { Sitemap } from "tanstack-router-sitemap";
|
||||
|
||||
// This will become a string literal union of all your routes
|
||||
export type TRoutes = FileRouteTypes["fullPaths"];
|
||||
|
||||
// Define your sitemap
|
||||
export const sitemap: Sitemap<TRoutes> = {
|
||||
siteUrl: "https://victoriaseguros.es",
|
||||
defaultPriority: 0.5,
|
||||
routes: {
|
||||
"/seguros": {
|
||||
priority: 1,
|
||||
changeFrequency: "daily",
|
||||
},
|
||||
},
|
||||
};
|
||||
60
tailwind.config.js
Normal file
60
tailwind.config.js
Normal file
@ -0,0 +1,60 @@
|
||||
// tailwind.config.js
|
||||
import { heroui } from "@heroui/theme";
|
||||
import { addIconSelectors } from "@iconify/tailwind";
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./public/**/*",
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"./node_modules/@heroui/theme/dist/components/(accordion|alert|autocomplete|avatar|badge|breadcrumbs|button|calendar|card|checkbox|chip|code|date-input|date-picker|divider|drawer|dropdown|form|image|input|input-otp|kbd|link|listbox|menu|modal|navbar|pagination|popover|progress|radio|ripple|scroll-shadow|select|skeleton|slider|snippet|spinner|table|tabs|user|spacer).js",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [heroui(
|
||||
{
|
||||
|
||||
"themes": {
|
||||
"light": {
|
||||
"colors": {
|
||||
"primary": {
|
||||
"DEFAULT": "#46a9b4",
|
||||
'50': '#f1fafa',
|
||||
'100': '#daf2f3',
|
||||
'200': '#bae3e7',
|
||||
'300': '#8bcfd5',
|
||||
'400': '#46a9b4',
|
||||
'500': '#3895a2',
|
||||
'600': '#317b89',
|
||||
'700': '#2e6470',
|
||||
'800': '#2c545e',
|
||||
'900': '#294650',
|
||||
'950': '#172e35',
|
||||
},
|
||||
"secondary": {
|
||||
"DEFAULT": "#946fa9",
|
||||
'50': '#faf8fc',
|
||||
'100': '#f4f0f7',
|
||||
'200': '#ece3f1',
|
||||
'300': '#dbcee4',
|
||||
'400': '#c4add3',
|
||||
'500': '#ad8dbf',
|
||||
'600': '#946fa9',
|
||||
'700': '#805d93',
|
||||
'800': '#6c5079',
|
||||
'900': '#584162',
|
||||
'950': '#3a2744',
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"disabledOpacity": "0.5"
|
||||
}
|
||||
},
|
||||
), addIconSelectors(["solar", "cib"])],
|
||||
};
|
||||
4
tailwind.config.mjs
Normal file
4
tailwind.config.mjs
Normal file
@ -0,0 +1,4 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
}
|
||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "ES2022",
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"],
|
||||
},
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user