Make footer popup the contact modal when contact link is clicked

This commit is contained in:
2025-11-21 20:44:18 -06:00
parent abc192887f
commit 6c14d771d6
11 changed files with 100 additions and 50 deletions

View File

@@ -14,6 +14,7 @@
"@angular/forms": "^20.3.0", "@angular/forms": "^20.3.0",
"@angular/platform-browser": "^20.3.0", "@angular/platform-browser": "^20.3.0",
"@angular/router": "^20.3.0", "@angular/router": "^20.3.0",
"@hugeicons/core-free-icons": "^2.0.0",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
@@ -1356,6 +1357,12 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/@hugeicons/core-free-icons": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-2.0.0.tgz",
"integrity": "sha512-OSfv5k0iB0yG61dcfK7jcf00AIK8EXyQOgtcNJzSBFvm88n9VOelkxihZHJnNwDUFpO/jZI3vZSVp6i1dmRvJQ==",
"license": "MIT"
},
"node_modules/@inquirer/ansi": { "node_modules/@inquirer/ansi": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",

View File

@@ -28,6 +28,7 @@
"@angular/forms": "^20.3.0", "@angular/forms": "^20.3.0",
"@angular/platform-browser": "^20.3.0", "@angular/platform-browser": "^20.3.0",
"@angular/router": "^20.3.0", "@angular/router": "^20.3.0",
"@hugeicons/core-free-icons": "^2.0.0",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",

View File

@@ -66,37 +66,7 @@
<div class="grid md:grid-cols-4 gap-8"> <div class="grid md:grid-cols-4 gap-8">
<div *ngFor="let plan of plans" <div *ngFor="let plan of plans"
[class]="'relative flex flex-col bg-slate-800/50 backdrop-blur-sm rounded-xl p-8 border-2 transition-all hover:transform hover:scale-105 ' + (plan.popular ? 'border-green-500 shadow-xl shadow-green-500/20' : 'border-slate-700 hover:border-green-500')"> [class]="'relative flex flex-col bg-slate-800/50 backdrop-blur-sm rounded-xl p-8 border-2 transition-all hover:transform hover:scale-105 ' + (plan.popular ? 'border-green-500 shadow-xl shadow-green-500/20' : 'border-slate-700 hover:border-green-500')">
<div class="flex-grow-1"> <app-price-card [plan]="plan" />
<div *ngIf="plan.popular"
class="absolute -top-4 left-1/2 transform -translate-x-1/2 bg-green-500 text-white px-4 py-1 rounded-full text-sm font-bold">
Most Popular
</div>
<div class="text-center mb-6">
<h3 class="text-2xl font-bold text-white mb-2">{{ plan.name }}</h3>
<div class="text-5xl font-bold text-white mb-2">{{ plan.price }}</div>
<div class="text-gray-400">/month</div>
</div>
<div class="space-y-3 mb-8">
<div class="text-center py-2 bg-slate-700/50 rounded-lg">
<div class="font-bold text-white">{{ plan.players }}</div>
</div>
<div class="text-center py-2 bg-slate-700/50 rounded-lg">
<div class="font-bold text-white">{{ plan.ram }}</div>
</div>
<div class="text-center py-2 bg-slate-700/50 rounded-lg">
<div class="font-bold text-white">{{ plan.storage }}</div>
</div>
</div>
<ul class="space-y-3 mb-8">
<li *ngFor="let feature of plan.features" class="flex items-center text-gray-300">
<svg class="w-5 h-5 text-green-400 mr-2 flex-shrink-0" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
{{ feature }}
</li>
</ul>
</div>
<button <button
[class]="'w-full py-3 rounded-lg font-bold transition-all ' + (plan.popular ? 'bg-green-500 hover:bg-green-600 text-white' : 'bg-slate-700 hover:bg-slate-600 text-white')"> [class]="'w-full py-3 rounded-lg font-bold transition-all ' + (plan.popular ? 'bg-green-500 hover:bg-green-600 text-white' : 'bg-slate-700 hover:bg-slate-600 text-white')">
@@ -123,7 +93,7 @@
</div> </div>
</section> </section>
<app-footer /> <app-footer [(contactOpen)]="contactOpen" />
<app-contact-modal /> <app-contact-modal [(open)]="contactOpen"/>
</div> </div>

View File

@@ -1,19 +1,21 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, OnDestroy, OnInit, HostListener } from '@angular/core'; import { Component, OnDestroy, OnInit, HostListener, signal } from '@angular/core';
import { Navigation } from './components/navigation/navigation'; import { Navigation } from './components/navigation/navigation';
import { Feature } from './interfaces/feature'; import { Feature } from './interfaces/feature';
import { Plan } from './interfaces/plan'; import { Plan } from './interfaces/plan';
import { Stat } from './interfaces/stat'; import { Stat } from './interfaces/stat';
import { Footer } from './components/footer/footer'; import { Footer } from './components/footer/footer';
import { ContactModal } from './components/contact-modal/contact-modal'; import { ContactModal } from './components/contact-modal/contact-modal';
import { PriceCard } from './components/price-card/price-card';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [CommonModule, Navigation, Footer, ContactModal], imports: [CommonModule, Navigation, Footer, ContactModal, PriceCard],
templateUrl: './app.html', templateUrl: './app.html',
styleUrl: './app.css' styleUrl: './app.css'
}) })
export class App implements OnInit, OnDestroy { export class App implements OnInit, OnDestroy {
contactOpen = signal<boolean>(false);
mobileMenuOpen = false; mobileMenuOpen = false;
scrolled = false; scrolled = false;

View File

@@ -1,13 +1,13 @@
<div class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm" <div class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm" [class.hidden]="!open()"
(ngClick)="toggleOpen()"> (click)="toggleOpen()">
<div class="relative w-full max-w-lg bg-gray-900 rounded-3xl border border-gray-800 p-8 shadow-2xl" <div class="relative w-full max-w-lg bg-gray-900 rounded-3xl border border-gray-800 p-8 shadow-2xl"
(ngClick)="toggleOpen()"> (click)="toggleOpen()">
<button (ngClick)="toggleOpen()" <button (click)="toggleOpen()"
class="absolute top-6 right-6 w-10 h-10 rounded-full border border-gray-700 flex items-center justify-center hover:border-violet-500 hover:text-violet-500 transition-all"> class="absolute top-6 right-6 w-10 h-10 rounded-full border border-gray-700 flex items-center justify-center hover:border-green-500 hover:text-green-500 transition-all">
<!-- <X class="w-5 h-5" /> --> <!-- <X class="w-5 h-5" /> -->
</button> </button>
<h2 class="text-3xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-violet-400 to-pink-400"> <h2 class="text-3xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-green-400 to-pink-400">
Get In Touch Get In Touch
</h2> </h2>
<p class="text-gray-400 mb-8"> <p class="text-gray-400 mb-8">
@@ -20,7 +20,7 @@
Name Name
</label> </label>
<input type="text" id="name" name="name" formControlName="name" required <input type="text" id="name" name="name" formControlName="name" required
class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-violet-500 transition-colors text-white" class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-green-500 transition-colors text-white"
placeholder="Your name" /> placeholder="Your name" />
</div> </div>
@@ -29,7 +29,7 @@
Email Email
</label> </label>
<input type="email" id="email" name="email" formControlName="email" required <input type="email" id="email" name="email" formControlName="email" required
class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-violet-500 transition-colors text-white" class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-green-500 transition-colors text-white"
placeholder="your@email.com" /> placeholder="your@email.com" />
</div> </div>
@@ -38,15 +38,15 @@
Message Message
</label> </label>
<input type="text" formControlName="message" required <input type="text" formControlName="message" required
class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-violet-500 transition-colors text-white resize-none" class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-green-500 transition-colors text-white resize-none"
placeholder="Tell me something" /> placeholder="Tell me something" />
<!-- <textarea id="message" name="message" formControlName="message" required rows="5" <!-- <textarea id="message" name="message" formControlName="message" required rows="5"
class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-violet-500 transition-colors text-white resize-none" class="w-full px-4 py-3 bg-black border border-gray-700 rounded-xl focus:outline-none focus:border-green-500 transition-colors text-white resize-none"
placeholder="Tell me about your project..."> --> placeholder="Tell me about your project..."> -->
</div> </div>
<button type="submit" <button type="submit"
class="w-full px-8 py-4 bg-gradient-to-r from-violet-600 to-purple-600 rounded-xl font-semibold hover:scale-[1.02] transition-transform flex items-center justify-center gap-2"> class="w-full px-8 py-4 bg-gradient-to-r from-green-300 to-green-500 rounded-xl font-semibold hover:scale-[1.02] transition-transform flex items-center justify-center gap-2">
Send Message Send Message
<!-- <ArrowRight class="w-5 h-5" /> --> <!-- <ArrowRight class="w-5 h-5" /> -->
</button> </button>

View File

@@ -8,7 +8,7 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
styles: ``, styles: ``,
}) })
export class ContactModal { export class ContactModal {
open = model<boolean>(false); open = model.required<boolean>();
contactForm = new FormGroup({ contactForm = new FormGroup({
name: new FormControl(''), name: new FormControl(''),
@@ -17,7 +17,7 @@ export class ContactModal {
}, { validators: Validators.required }) }, { validators: Validators.required })
toggleOpen() { toggleOpen() {
this.open.update(value => !value); this.open.update(prev => !prev);
} }
handleSubmit() { handleSubmit() {

View File

@@ -11,7 +11,7 @@
<div class="flex justify-center space-x-6 text-sm"> <div class="flex justify-center space-x-6 text-sm">
<a href="#" class="hover:text-white transition">Terms</a> <a href="#" class="hover:text-white transition">Terms</a>
<a href="#" class="hover:text-white transition">Privacy</a> <a href="#" class="hover:text-white transition">Privacy</a>
<a href="#" class="hover:text-white transition">Contact</a> <a (click)="toggleContactModal()" class="hover:text-white transition">Contact</a>
</div> </div>
</div> </div>
</footer> </footer>

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'; import { Component, model } from '@angular/core';
@Component({ @Component({
selector: 'app-footer', selector: 'app-footer',
@@ -7,5 +7,9 @@ import { Component } from '@angular/core';
styles: ``, styles: ``,
}) })
export class Footer { export class Footer {
contactOpen = model.required<boolean>();
toggleContactModal() {
this.contactOpen.update(prev => !prev);
}
} }

View File

@@ -0,0 +1,30 @@
<div class="flex-grow-1">
<div *ngIf="plan().popular"
class="absolute -top-4 left-1/2 transform -translate-x-1/2 bg-green-500 text-white px-4 py-1 rounded-full text-sm font-bold">
Most Popular
</div>
<div class="text-center mb-6">
<h3 class="text-2xl font-bold text-white mb-2">{{ plan().name }}</h3>
<div class="text-5xl font-bold text-white mb-2">{{ plan().price }}</div>
<div class="text-gray-400">/month</div>
</div>
<div class="space-y-3 mb-8">
<div class="text-center py-2 bg-slate-700/50 rounded-lg">
<div class="font-bold text-white">{{ plan().players }}</div>
</div>
<div class="text-center py-2 bg-slate-700/50 rounded-lg">
<div class="font-bold text-white">{{ plan().ram }}</div>
</div>
<div class="text-center py-2 bg-slate-700/50 rounded-lg">
<div class="font-bold text-white">{{ plan().storage }}</div>
</div>
</div>
<ul class="space-y-3 mb-8">
<li *ngFor="let feature of plan().features" class="flex items-center text-gray-300">
<svg class="w-5 h-5 text-green-400 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
{{ feature }}
</li>
</ul>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PriceCard } from './price-card';
describe('PriceCard', () => {
let component: PriceCard;
let fixture: ComponentFixture<PriceCard>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PriceCard]
})
.compileComponents();
fixture = TestBed.createComponent(PriceCard);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,13 @@
import { Component, input } from '@angular/core';
import { Plan } from '../../interfaces/plan';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-price-card',
imports: [CommonModule],
templateUrl: './price-card.html',
styles: ``,
})
export class PriceCard {
plan = input.required<Plan>();
}