Compare commits

..

2 Commits

Author SHA1 Message Date
8efee17cb6 bump
Some checks failed
Docker Deploy / build-and-push (push) Failing after 2m32s
2025-06-18 00:51:14 -06:00
82a9085446 1.0 2025-06-18 00:50:45 -06:00
11 changed files with 1360 additions and 1558 deletions

View File

@ -1,2 +1 @@
# Atash Website

View File

@ -1,17 +1,21 @@
// @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
export default defineConfig({
integrations: [tailwind(), solidJs()],
integrations: [solidJs()],
adapter: node({
mode: 'standalone'
})
mode: "standalone",
}),
vite: {
plugins: [tailwindcss()],
},
});

View File

@ -1,7 +1,7 @@
{
"name": "atashdotdev",
"type": "module",
"version": "0.0.1",
"version": "1.0.0",
"scripts": {
"dev": "astro dev",
"build": "astro build",
@ -11,15 +11,15 @@
"dependencies": {
"@astrojs/node": "^9.2.2",
"@astrojs/solid-js": "^5.1.0",
"@astrojs/tailwind": "^6.0.2",
"astro": "^5.8.0",
"nodemailer": "^6.9.8",
"solid-js": "^1.9.4",
"tailwindcss": "^3.0.24"
"@tailwindcss/vite": "^4.1.10",
"astro": "^5.9.4",
"nodemailer": "^7.0.3",
"solid-js": "^1.9.7",
"tailwindcss": "^4.1.10"
},
"devDependencies": {
"@types/node": "^22.10.10",
"@types/nodemailer": "^6.4.14",
"daisyui": "^4.12.23"
"@types/node": "^24.0.3",
"@types/nodemailer": "^6.4.17",
"daisyui": "^5.0.43"
}
}

2253
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,13 @@ import { createSignal, type Component } from "solid-js";
import { Show } from "solid-js/web";
const ContactForm: Component = () => {
const [firstName, setFirstName] = createSignal("");
const [lastName, setLastName] = createSignal("");
const [email, setEmail] = createSignal("");
const [company, setCompany] = createSignal("");
const [service, setService] = createSignal("");
const [budget, setBudget] = createSignal("");
const [message, setMessage] = createSignal("");
const [name, setName] = createSignal("");
const [status, setStatus] = createSignal<
"idle" | "sending" | "success" | "error"
>("idle");
@ -20,8 +24,15 @@ const ContactForm: Component = () => {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
subject: `New Contact Form Message from ${name()}`,
message: `From: ${name()}\nEmail: ${email()}\n\nMessage:\n${message()}`,
subject: `New Contact Form Message from ${firstName()} ${lastName()}`,
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");
setName("");
setFirstName("");
setLastName("");
setEmail("");
setCompany("");
setService("");
setBudget("");
setMessage("");
setTimeout(() => setStatus("idle"), 3000);
} catch (error) {
@ -47,47 +62,115 @@ const ContactForm: Component = () => {
return (
<form class="space-y-6" onSubmit={handleSubmit}>
<div class="form-control w-full">
<label for="name" class="label">
<span class="label-text text-neutral-content">Name</span>
</label>
<input
type="text"
id="name"
required
class="input input-bordered w-full"
placeholder="Your Name"
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
disabled={status() === "sending"}
/>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">First Name *</span>
</label>
<input
type="text"
name="firstName"
class="input input-bordered w-full"
required
value={firstName()}
onInput={(e) => setFirstName(e.currentTarget.value)}
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 class="form-control w-full">
<label for="email" class="label">
<span class="label-text text-neutral-content">Email</span>
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Email Address *</span>
</label>
<input
type="email"
id="email"
required
name="email"
class="input input-bordered w-full"
placeholder="your@email.com"
required
value={email()}
onInput={(e) => setEmail(e.currentTarget.value)}
disabled={status() === "sending"}
/>
</div>
<div class="form-control w-full">
<label for="message" class="label">
<span class="label-text text-neutral-content">Message</span>
<div class="form-control">
<label class="label">
<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>
<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
class="textarea textarea-bordered h-32"
placeholder="How can we help you?"
value={message()}
onInput={(e) => setMessage(e.currentTarget.value)}
disabled={status() === "sending"}
@ -134,10 +217,10 @@ const ContactForm: Component = () => {
</div>
</Show>
<div class="card-actions justify-end">
<div class="form-control pt-4">
<button
type="submit"
class="btn btn-primary"
class="btn btn-primary btn-lg w-full"
disabled={status() === "sending"}
>
{status() === "sending" ? (

View File

@ -1,7 +1,7 @@
---
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import { siteConfig } from "../config/site";
import "./src/styles/global.css";
interface Props {
title?: string;
@ -29,7 +29,6 @@ const metaTitle =
<title>{metaTitle}</title>
</head>
<body class="min-h-screen flex flex-col">
<Header />
<main class="container mx-auto px-4 py-8 flex-grow">
<slot />
</main>

View File

@ -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>

View File

@ -1,6 +1,7 @@
---
import Layout from "../layouts/Layout.astro";
import { siteConfig } from "../config/site";
import ContactForm from "../components/ContactForm.tsx";
const pageMetaInfo = {
title: siteConfig.name,
@ -9,97 +10,269 @@ const pageMetaInfo = {
---
<Layout title={pageMetaInfo.title} description={pageMetaInfo.description}>
<div class="max-w-4xl mx-auto">
<section
class="card bg-neutral text-neutral-content shadow-xl"
aria-labelledby="welcome-heading"
>
<div class="card-body">
<h1 id="welcome-heading" class="card-title text-3xl">
{siteConfig.hero.title}
<!-- Hero Section -->
<section
class="hero min-h-[60vh] bg-gradient-to-br from-primary to-primary-focus"
>
<div class="hero-content text-center text-primary-content">
<div class="max-w-4xl">
<h1 class="text-5xl font-bold mb-6 leading-tight">
{siteConfig.name}
</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>
</section>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-8">
{
siteConfig.featureCards.cards.map((card) => (
<article
class={
card.variant === "primary"
? "card bg-primary text-primary-content"
: "card bg-secondary text-secondary-content"
}
>
<div class="card-body">
<h2 class="card-title">{card.title}</h2>
</div>
</article>
))
}
</div>
{
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">
<!-- About Section -->
<section class="mb-20">
<div class="hero bg-base-200 rounded-2xl">
<div class="hero-content text-center py-16">
<div class="max-w-3xl">
<h2 class="text-3xl font-bold mb-6">
Why Choose Atash Consulting?
</h2>
<p
class="text-lg mb-8 text-base-content/80 leading-relaxed"
>
With over a decade of experience in the software
industry, we bring deep technical expertise and a
commitment to excellence to every project.
</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
<div class="text-center">
<div
class="stat-title"
id={`${stat.title.toLowerCase()}-stat`}
class="w-16 h-16 bg-primary rounded-full flex items-center justify-center mx-auto mb-4"
>
{stat.title}
</div>
<div
class="stat-value"
aria-labelledby={`${stat.title.toLowerCase()}-stat`}
>
{stat.value}
</div>
{stat.change && (
<div
class="stat-desc"
aria-label={`${
stat.change.direction === "up"
? "Increase"
: "Decrease"
} of ${stat.change.value} (${stat.change.percentage}%)`}
<svg
class="w-8 h-8 text-primary-content"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
{stat.change.direction === "up"
? "↗︎"
: "↘︎"}{" "}
{stat.change.value} (
{stat.change.percentage}%)
</div>
)}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 10V3L4 14h7v7l9-11h-7z"
></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>
))}
</section>
)
}
<div class="text-center">
<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
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>
<!-- Contact Section -->
<section id="contact" class="mb-20">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4">Get In Touch</h2>
<p class="text-lg text-base-content/70 max-w-2xl mx-auto">
Ready to start your project? Let's discuss how we can help
transform your ideas into robust, scalable software
solutions.
</p>
</div>
<div class="max-w-2xl mx-auto">
<div class="card bg-base-100 shadow-xl border border-base-200">
<div class="card-body">
<ContactForm client:load />
</div>
</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>
</section>

View File

@ -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
View 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;
}

View File

@ -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"],
},
};