1.0
This commit is contained in:
@ -1,17 +1,21 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from "astro/config";
|
||||||
|
|
||||||
import tailwind from '@astrojs/tailwind';
|
import solidJs from "@astrojs/solid-js";
|
||||||
|
|
||||||
import solidJs from '@astrojs/solid-js';
|
import node from "@astrojs/node";
|
||||||
|
|
||||||
import node from '@astrojs/node';
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [tailwind(), solidJs()],
|
integrations: [solidJs()],
|
||||||
|
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: 'standalone'
|
mode: "standalone",
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()],
|
||||||
|
},
|
||||||
});
|
});
|
16
package.json
16
package.json
@ -11,15 +11,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/node": "^9.2.2",
|
"@astrojs/node": "^9.2.2",
|
||||||
"@astrojs/solid-js": "^5.1.0",
|
"@astrojs/solid-js": "^5.1.0",
|
||||||
"@astrojs/tailwind": "^6.0.2",
|
"@tailwindcss/vite": "^4.1.10",
|
||||||
"astro": "^5.8.0",
|
"astro": "^5.9.4",
|
||||||
"nodemailer": "^6.9.8",
|
"nodemailer": "^7.0.3",
|
||||||
"solid-js": "^1.9.4",
|
"solid-js": "^1.9.7",
|
||||||
"tailwindcss": "^3.0.24"
|
"tailwindcss": "^4.1.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.10.10",
|
"@types/node": "^24.0.3",
|
||||||
"@types/nodemailer": "^6.4.14",
|
"@types/nodemailer": "^6.4.17",
|
||||||
"daisyui": "^4.12.23"
|
"daisyui": "^5.0.43"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2253
pnpm-lock.yaml
generated
2253
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -2,9 +2,13 @@ import { createSignal, type Component } from "solid-js";
|
|||||||
import { Show } from "solid-js/web";
|
import { Show } from "solid-js/web";
|
||||||
|
|
||||||
const ContactForm: Component = () => {
|
const ContactForm: Component = () => {
|
||||||
|
const [firstName, setFirstName] = createSignal("");
|
||||||
|
const [lastName, setLastName] = createSignal("");
|
||||||
const [email, setEmail] = createSignal("");
|
const [email, setEmail] = createSignal("");
|
||||||
|
const [company, setCompany] = createSignal("");
|
||||||
|
const [service, setService] = createSignal("");
|
||||||
|
const [budget, setBudget] = createSignal("");
|
||||||
const [message, setMessage] = createSignal("");
|
const [message, setMessage] = createSignal("");
|
||||||
const [name, setName] = createSignal("");
|
|
||||||
const [status, setStatus] = createSignal<
|
const [status, setStatus] = createSignal<
|
||||||
"idle" | "sending" | "success" | "error"
|
"idle" | "sending" | "success" | "error"
|
||||||
>("idle");
|
>("idle");
|
||||||
@ -20,8 +24,15 @@ const ContactForm: Component = () => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
subject: `New Contact Form Message from ${name()}`,
|
subject: `New Contact Form Message from ${firstName()} ${lastName()}`,
|
||||||
message: `From: ${name()}\nEmail: ${email()}\n\nMessage:\n${message()}`,
|
message: `From: ${firstName()} ${lastName()}
|
||||||
|
Email: ${email()}
|
||||||
|
Company: ${company() || "Not specified"}
|
||||||
|
Service Needed: ${service() || "Not specified"}
|
||||||
|
Budget: ${budget() || "Not specified"}
|
||||||
|
|
||||||
|
Project Details:
|
||||||
|
${message()}`,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -32,8 +43,12 @@ const ContactForm: Component = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setStatus("success");
|
setStatus("success");
|
||||||
setName("");
|
setFirstName("");
|
||||||
|
setLastName("");
|
||||||
setEmail("");
|
setEmail("");
|
||||||
|
setCompany("");
|
||||||
|
setService("");
|
||||||
|
setBudget("");
|
||||||
setMessage("");
|
setMessage("");
|
||||||
setTimeout(() => setStatus("idle"), 3000);
|
setTimeout(() => setStatus("idle"), 3000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -47,47 +62,115 @@ const ContactForm: Component = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form class="space-y-6" onSubmit={handleSubmit}>
|
<form class="space-y-6" onSubmit={handleSubmit}>
|
||||||
<div class="form-control w-full">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<label for="name" class="label">
|
<div class="form-control">
|
||||||
<span class="label-text text-neutral-content">Name</span>
|
<label class="label">
|
||||||
</label>
|
<span class="label-text font-semibold">First Name *</span>
|
||||||
<input
|
</label>
|
||||||
type="text"
|
<input
|
||||||
id="name"
|
type="text"
|
||||||
required
|
name="firstName"
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
placeholder="Your Name"
|
required
|
||||||
value={name()}
|
value={firstName()}
|
||||||
onInput={(e) => setName(e.currentTarget.value)}
|
onInput={(e) => setFirstName(e.currentTarget.value)}
|
||||||
disabled={status() === "sending"}
|
disabled={status() === "sending"}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-semibold">Last Name *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="lastName"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
required
|
||||||
|
value={lastName()}
|
||||||
|
onInput={(e) => setLastName(e.currentTarget.value)}
|
||||||
|
disabled={status() === "sending"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control w-full">
|
<div class="form-control">
|
||||||
<label for="email" class="label">
|
<label class="label">
|
||||||
<span class="label-text text-neutral-content">Email</span>
|
<span class="label-text font-semibold">Email Address *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
name="email"
|
||||||
required
|
|
||||||
class="input input-bordered w-full"
|
class="input input-bordered w-full"
|
||||||
placeholder="your@email.com"
|
required
|
||||||
value={email()}
|
value={email()}
|
||||||
onInput={(e) => setEmail(e.currentTarget.value)}
|
onInput={(e) => setEmail(e.currentTarget.value)}
|
||||||
disabled={status() === "sending"}
|
disabled={status() === "sending"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control w-full">
|
<div class="form-control">
|
||||||
<label for="message" class="label">
|
<label class="label">
|
||||||
<span class="label-text text-neutral-content">Message</span>
|
<span class="label-text font-semibold">Company</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="company"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
value={company()}
|
||||||
|
onInput={(e) => setCompany(e.currentTarget.value)}
|
||||||
|
disabled={status() === "sending"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-semibold">Service Needed</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="service"
|
||||||
|
class="select select-bordered w-full"
|
||||||
|
value={service()}
|
||||||
|
onChange={(e) => setService(e.currentTarget.value)}
|
||||||
|
disabled={status() === "sending"}
|
||||||
|
>
|
||||||
|
<option value="">Select a service...</option>
|
||||||
|
<option value="web-development">Web Development</option>
|
||||||
|
<option value="mobile-development">Mobile App Development</option>
|
||||||
|
<option value="devops">DevOps</option>
|
||||||
|
<option value="it-support">IT Support Processes</option>
|
||||||
|
<option value="consultation">General Consultation</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-semibold">Project Budget</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="budget"
|
||||||
|
class="select select-bordered w-full"
|
||||||
|
value={budget()}
|
||||||
|
onChange={(e) => setBudget(e.currentTarget.value)}
|
||||||
|
disabled={status() === "sending"}
|
||||||
|
>
|
||||||
|
<option value="">Select budget range...</option>
|
||||||
|
<option value="under-5k">Under $5,000</option>
|
||||||
|
<option value="5k-15k">$5,000 - $15,000</option>
|
||||||
|
<option value="15k-50k">$15,000 - $50,000</option>
|
||||||
|
<option value="50k-plus">$50,000+</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="project-details">
|
||||||
|
<span class="label-text font-semibold">Project Details *</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="message"
|
id="project-details"
|
||||||
|
name="message"
|
||||||
|
class="textarea textarea-bordered h-32 w-full resize-none"
|
||||||
|
placeholder="Tell us about your project requirements, timeline, and any specific needs..."
|
||||||
required
|
required
|
||||||
class="textarea textarea-bordered h-32"
|
|
||||||
placeholder="How can we help you?"
|
|
||||||
value={message()}
|
value={message()}
|
||||||
onInput={(e) => setMessage(e.currentTarget.value)}
|
onInput={(e) => setMessage(e.currentTarget.value)}
|
||||||
disabled={status() === "sending"}
|
disabled={status() === "sending"}
|
||||||
@ -134,10 +217,10 @@ const ContactForm: Component = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div class="card-actions justify-end">
|
<div class="form-control pt-4">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary btn-lg w-full"
|
||||||
disabled={status() === "sending"}
|
disabled={status() === "sending"}
|
||||||
>
|
>
|
||||||
{status() === "sending" ? (
|
{status() === "sending" ? (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import Header from "../components/Header.astro";
|
|
||||||
import Footer from "../components/Footer.astro";
|
import Footer from "../components/Footer.astro";
|
||||||
import { siteConfig } from "../config/site";
|
import { siteConfig } from "../config/site";
|
||||||
|
import "./src/styles/global.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -29,7 +29,6 @@ const metaTitle =
|
|||||||
<title>{metaTitle}</title>
|
<title>{metaTitle}</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen flex flex-col">
|
<body class="min-h-screen flex flex-col">
|
||||||
<Header />
|
|
||||||
<main class="container mx-auto px-4 py-8 flex-grow">
|
<main class="container mx-auto px-4 py-8 flex-grow">
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
import Layout from "../layouts/Layout.astro";
|
|
||||||
import ContactForm from "../components/ContactForm";
|
|
||||||
import { siteConfig } from "../config/site";
|
|
||||||
|
|
||||||
const pageMetaInfo = {
|
|
||||||
title: "Contact | " + siteConfig.name,
|
|
||||||
description: "Get in touch with us for your software development needs",
|
|
||||||
};
|
|
||||||
---
|
|
||||||
|
|
||||||
<Layout title={pageMetaInfo.title} description={pageMetaInfo.description}>
|
|
||||||
<div class="max-w-2xl mx-auto">
|
|
||||||
<section class="card bg-neutral text-neutral-content shadow-xl">
|
|
||||||
<div class="card-body">
|
|
||||||
<h1 class="card-title text-3xl">Contact Us</h1>
|
|
||||||
<p class="text-lg">
|
|
||||||
Ready to get started? Send us a message and we'll get back
|
|
||||||
to you shortly.
|
|
||||||
</p>
|
|
||||||
<ContactForm client:load />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
import Layout from "../layouts/Layout.astro";
|
import Layout from "../layouts/Layout.astro";
|
||||||
import { siteConfig } from "../config/site";
|
import { siteConfig } from "../config/site";
|
||||||
|
import ContactForm from "../components/ContactForm.tsx";
|
||||||
|
|
||||||
const pageMetaInfo = {
|
const pageMetaInfo = {
|
||||||
title: siteConfig.name,
|
title: siteConfig.name,
|
||||||
@ -9,97 +10,269 @@ const pageMetaInfo = {
|
|||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={pageMetaInfo.title} description={pageMetaInfo.description}>
|
<Layout title={pageMetaInfo.title} description={pageMetaInfo.description}>
|
||||||
<div class="max-w-4xl mx-auto">
|
<!-- Hero Section -->
|
||||||
<section
|
<section
|
||||||
class="card bg-neutral text-neutral-content shadow-xl"
|
class="hero min-h-[60vh] bg-gradient-to-br from-primary to-primary-focus"
|
||||||
aria-labelledby="welcome-heading"
|
>
|
||||||
>
|
<div class="hero-content text-center text-primary-content">
|
||||||
<div class="card-body">
|
<div class="max-w-4xl">
|
||||||
<h1 id="welcome-heading" class="card-title text-3xl">
|
<h1 class="text-5xl font-bold mb-6 leading-tight">
|
||||||
{siteConfig.hero.title}
|
{siteConfig.name}
|
||||||
</h1>
|
</h1>
|
||||||
<p>{siteConfig.hero.description}</p>
|
<p
|
||||||
|
class="text-xl mb-8 opacity-90 max-w-2xl mx-auto leading-relaxed"
|
||||||
|
>
|
||||||
|
{siteConfig.hero.description} — Delivering reliable, scalable
|
||||||
|
solutions tailored to your business needs.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<a href="#contact" class="btn btn-secondary btn-lg">
|
||||||
|
Start Your Project
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#services"
|
||||||
|
class="btn btn-outline btn-lg text-white border-white hover:bg-white hover:text-primary"
|
||||||
|
>
|
||||||
|
View Services
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="max-w-6xl mx-auto px-4 py-16">
|
||||||
|
<!-- Services Grid -->
|
||||||
|
<section id="services" class="mb-20">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<h2 class="text-4xl font-bold mb-4">Our Services</h2>
|
||||||
|
<p class="text-lg text-base-content/70 max-w-2xl mx-auto">
|
||||||
|
Comprehensive solutions designed to drive your business
|
||||||
|
forward
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
{
|
||||||
|
siteConfig.featureCards.cards.map((card, index) => (
|
||||||
|
<div class="card bg-base-100 shadow-lg hover:shadow-xl transition-all duration-300 border border-base-200">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="flex items-start gap-4">
|
||||||
|
<div
|
||||||
|
class={`
|
||||||
|
w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0
|
||||||
|
${
|
||||||
|
card.variant === "primary"
|
||||||
|
? "bg-primary text-primary-content"
|
||||||
|
: card.variant === "secondary"
|
||||||
|
? "bg-secondary text-secondary-content"
|
||||||
|
: "bg-accent text-accent-content"
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{index === 0 && (
|
||||||
|
<svg
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{index === 1 && (
|
||||||
|
<svg
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{index === 2 && (
|
||||||
|
<svg
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{index === 3 && (
|
||||||
|
<svg
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192L5.636 18.364M12 2.25a9.75 9.75 0 109.75 9.75A9.75 9.75 0 0012 2.25z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h3 class="card-title text-xl mb-3">
|
||||||
|
{card.title}
|
||||||
|
</h3>
|
||||||
|
<p class="text-base-content/70 leading-relaxed">
|
||||||
|
{card.content}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-8">
|
<!-- About Section -->
|
||||||
{
|
<section class="mb-20">
|
||||||
siteConfig.featureCards.cards.map((card) => (
|
<div class="hero bg-base-200 rounded-2xl">
|
||||||
<article
|
<div class="hero-content text-center py-16">
|
||||||
class={
|
<div class="max-w-3xl">
|
||||||
card.variant === "primary"
|
<h2 class="text-3xl font-bold mb-6">
|
||||||
? "card bg-primary text-primary-content"
|
Why Choose Atash Consulting?
|
||||||
: "card bg-secondary text-secondary-content"
|
</h2>
|
||||||
}
|
<p
|
||||||
>
|
class="text-lg mb-8 text-base-content/80 leading-relaxed"
|
||||||
<div class="card-body">
|
>
|
||||||
<h2 class="card-title">{card.title}</h2>
|
With over a decade of experience in the software
|
||||||
</div>
|
industry, we bring deep technical expertise and a
|
||||||
</article>
|
commitment to excellence to every project.
|
||||||
))
|
</p>
|
||||||
}
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
|
||||||
</div>
|
<div class="text-center">
|
||||||
|
|
||||||
{
|
|
||||||
siteConfig.statistics.enabled &&
|
|
||||||
siteConfig.statistics.stats.length > 0 && (
|
|
||||||
<section
|
|
||||||
class="stats shadow mt-8 w-full bg-neutral text-neutral-content"
|
|
||||||
aria-label={siteConfig.statistics.title}
|
|
||||||
>
|
|
||||||
{siteConfig.statistics.stats.map((stat) => (
|
|
||||||
<div class="stat">
|
|
||||||
<div
|
<div
|
||||||
class="stat-title"
|
class="w-16 h-16 bg-primary rounded-full flex items-center justify-center mx-auto mb-4"
|
||||||
id={`${stat.title.toLowerCase()}-stat`}
|
|
||||||
>
|
>
|
||||||
{stat.title}
|
<svg
|
||||||
</div>
|
class="w-8 h-8 text-primary-content"
|
||||||
<div
|
fill="none"
|
||||||
class="stat-value"
|
stroke="currentColor"
|
||||||
aria-labelledby={`${stat.title.toLowerCase()}-stat`}
|
viewBox="0 0 24 24"
|
||||||
>
|
|
||||||
{stat.value}
|
|
||||||
</div>
|
|
||||||
{stat.change && (
|
|
||||||
<div
|
|
||||||
class="stat-desc"
|
|
||||||
aria-label={`${
|
|
||||||
stat.change.direction === "up"
|
|
||||||
? "Increase"
|
|
||||||
: "Decrease"
|
|
||||||
} of ${stat.change.value} (${stat.change.percentage}%)`}
|
|
||||||
>
|
>
|
||||||
{stat.change.direction === "up"
|
<path
|
||||||
? "↗︎"
|
stroke-linecap="round"
|
||||||
: "↘︎"}{" "}
|
stroke-linejoin="round"
|
||||||
{stat.change.value} (
|
stroke-width="2"
|
||||||
{stat.change.percentage}%)
|
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||||||
</div>
|
></path>
|
||||||
)}
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="font-semibold mb-2">
|
||||||
|
Fast Delivery
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-base-content/70">
|
||||||
|
Quick turnaround without compromising
|
||||||
|
quality
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div class="text-center">
|
||||||
</section>
|
<div
|
||||||
)
|
class="w-16 h-16 bg-secondary rounded-full flex items-center justify-center mx-auto mb-4"
|
||||||
}
|
>
|
||||||
|
<svg
|
||||||
|
class="w-8 h-8 text-secondary-content"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="font-semibold mb-2">
|
||||||
|
Quality Assured
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-base-content/70">
|
||||||
|
Rigorous testing and quality control
|
||||||
|
processes
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<div
|
||||||
|
class="w-16 h-16 bg-accent rounded-full flex items-center justify-center mx-auto mb-4"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-8 h-8 text-accent-content"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="font-semibold mb-2">
|
||||||
|
Expert Support
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-base-content/70">
|
||||||
|
Ongoing support and maintenance services
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section
|
<!-- Contact Section -->
|
||||||
class="card bg-neutral text-neutral-content shadow-xl mt-8 hidden lg:block"
|
<section id="contact" class="mb-20">
|
||||||
aria-labelledby="contact-heading"
|
<div class="text-center mb-12">
|
||||||
>
|
<h2 class="text-4xl font-bold mb-4">Get In Touch</h2>
|
||||||
<div class="card-body">
|
<p class="text-lg text-base-content/70 max-w-2xl mx-auto">
|
||||||
<h2 id="contact-heading" class="card-title">
|
Ready to start your project? Let's discuss how we can help
|
||||||
{siteConfig.contact.title}
|
transform your ideas into robust, scalable software
|
||||||
</h2>
|
solutions.
|
||||||
<p>{siteConfig.contact.description}</p>
|
</p>
|
||||||
<div class="card-actions justify-end">
|
</div>
|
||||||
<button
|
|
||||||
class="btn btn-primary"
|
<div class="max-w-2xl mx-auto">
|
||||||
onclick={`window.location.href='${siteConfig.contact.cta.href}'`}
|
<div class="card bg-base-100 shadow-xl border border-base-200">
|
||||||
aria-label={siteConfig.contact.cta.ariaLabel}
|
<div class="card-body">
|
||||||
>
|
<ContactForm client:load />
|
||||||
{siteConfig.contact.cta.text}
|
</div>
|
||||||
</button>
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-8">
|
||||||
|
<p class="text-base-content/60">
|
||||||
|
We'll get back to you within 24 hours
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
---
|
|
||||||
import Layout from "../layouts/Layout.astro";
|
|
||||||
import { siteConfig } from "../config/site";
|
|
||||||
|
|
||||||
const pageMetaInfo = {
|
|
||||||
title: "Services | " + siteConfig.name,
|
|
||||||
description:
|
|
||||||
"Comprehensive software development and IT services in Edmonton, Alberta",
|
|
||||||
};
|
|
||||||
---
|
|
||||||
|
|
||||||
<Layout title={pageMetaInfo.title} description={pageMetaInfo.description}>
|
|
||||||
<div class="max-w-5xl mx-auto flex flex-col gap-4">
|
|
||||||
<!-- Increased from 4xl to 5xl -->
|
|
||||||
<section class="card bg-neutral text-neutral-content shadow-xl">
|
|
||||||
<div class="card-body">
|
|
||||||
<h1 class="card-title text-3xl">Our Services</h1>
|
|
||||||
<p class="text-lg">
|
|
||||||
Comprehensive software development and IT solutions tailored
|
|
||||||
to your needs.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
{
|
|
||||||
siteConfig.featureCards.cards.map((service, index) => (
|
|
||||||
<section class="card bg-neutral text-neutral-content shadow-xl basis-[calc(50%-0.5rem)]">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<div
|
|
||||||
class={`badge badge-${service.variant} badge-lg`}
|
|
||||||
>
|
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
<h2 class="card-title text-2xl">
|
|
||||||
{service.title}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<p class="text-lg mt-4">{service.content}</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section
|
|
||||||
class="card bg-neutral text-neutral-content shadow-xl mt-8 hidden lg:block"
|
|
||||||
aria-labelledby="contact-heading"
|
|
||||||
>
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 id="contact-heading" class="card-title">
|
|
||||||
{siteConfig.contact.title}
|
|
||||||
</h2>
|
|
||||||
<p>{siteConfig.contact.description}</p>
|
|
||||||
<div class="card-actions justify-end">
|
|
||||||
<button
|
|
||||||
class="btn btn-primary"
|
|
||||||
onclick={`window.location.href='${siteConfig.contact.cta.href}'`}
|
|
||||||
aria-label={siteConfig.contact.cta.ariaLabel}
|
|
||||||
>
|
|
||||||
{siteConfig.contact.cta.text}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
37
src/styles/global.css
Normal file
37
src/styles/global.css
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@plugin "daisyui";
|
||||||
|
|
||||||
|
@plugin "daisyui/theme" {
|
||||||
|
name: "sunset";
|
||||||
|
default: false;
|
||||||
|
prefersdark: false;
|
||||||
|
color-scheme: "dark";
|
||||||
|
--color-base-100: oklch(22% 0.019 237.69);
|
||||||
|
--color-base-200: oklch(20% 0.019 237.69);
|
||||||
|
--color-base-300: oklch(18% 0.019 237.69);
|
||||||
|
--color-base-content: oklch(77.383% 0.043 245.096);
|
||||||
|
--color-primary: oklch(74.703% 0.158 39.947);
|
||||||
|
--color-primary-content: oklch(14.94% 0.031 39.947);
|
||||||
|
--color-secondary: oklch(72.537% 0.177 2.72);
|
||||||
|
--color-secondary-content: oklch(14.507% 0.035 2.72);
|
||||||
|
--color-accent: oklch(71.294% 0.166 299.844);
|
||||||
|
--color-accent-content: oklch(14.258% 0.033 299.844);
|
||||||
|
--color-neutral: oklch(26% 0.019 237.69);
|
||||||
|
--color-neutral-content: oklch(70% 0.019 237.69);
|
||||||
|
--color-info: oklch(85.559% 0.085 206.015);
|
||||||
|
--color-info-content: oklch(17.111% 0.017 206.015);
|
||||||
|
--color-success: oklch(85.56% 0.085 144.778);
|
||||||
|
--color-success-content: oklch(17.112% 0.017 144.778);
|
||||||
|
--color-warning: oklch(85.569% 0.084 74.427);
|
||||||
|
--color-warning-content: oklch(17.113% 0.016 74.427);
|
||||||
|
--color-error: oklch(85.511% 0.078 16.886);
|
||||||
|
--color-error-content: oklch(17.102% 0.015 16.886);
|
||||||
|
--radius-selector: 1rem;
|
||||||
|
--radius-field: 0.5rem;
|
||||||
|
--radius-box: 1rem;
|
||||||
|
--size-selector: 0.25rem;
|
||||||
|
--size-field: 0.25rem;
|
||||||
|
--border: 1px;
|
||||||
|
--depth: 0;
|
||||||
|
--noise: 0;
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
export default {
|
|
||||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [require("daisyui")],
|
|
||||||
daisyui: {
|
|
||||||
themes: ["coffee", "dark", "sunset"],
|
|
||||||
},
|
|
||||||
};
|
|
Reference in New Issue
Block a user