Compare commits
17 Commits
dfa0c29098
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e451d0c0ac | |||
| 72e27eaedc | |||
| 16ddd1c8a3 | |||
| 3e4efc2fd2 | |||
| 1a845b3f2e | |||
| 38e018d034 | |||
| ddfa97b5d0 | |||
| 668741d028 | |||
| d8d9a9bfad | |||
| 0e9b847a47 | |||
| 513ef1724b | |||
| 79f84c18ee | |||
| 93b1f7fb9c | |||
| 0b5094d200 | |||
| 1b3cc5b750 | |||
| 9bc001b439 | |||
| 6990640f48 |
83
.github/workflows/build.yml
vendored
Normal file
83
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
name: Build Website
|
||||
run-name: Build Website
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build NPM Project
|
||||
runs-on: ubuntu-latest
|
||||
container: node
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: ztimson/actions/clone@develop
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Build Project
|
||||
run: npm run build
|
||||
|
||||
- name: Upload Artifacts
|
||||
if: ${{inputs.artifacts}} != "false"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: website
|
||||
path: dist
|
||||
retention-days: 7
|
||||
|
||||
tag:
|
||||
name: Tag Version
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
container: node
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: ztimson/actions/clone@develop
|
||||
|
||||
- name: Get Version Number
|
||||
run: echo "VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')" >> $GITHUB_ENV
|
||||
|
||||
- name: Tag Version
|
||||
uses: ztimson/actions/tag@develop
|
||||
with:
|
||||
tag: ${{env.VERSION}}
|
||||
|
||||
container:
|
||||
name: Build Container
|
||||
runs-on: ubuntu-latest
|
||||
container: docker
|
||||
steps:
|
||||
- name: Build Container
|
||||
run: |
|
||||
git clone -b "${{github.ref_name}}" "$(echo ${{github.server_url}}/${{github.repository}}.git | sed s%://%://${{github.token}}@% )" .
|
||||
DOCKER_HUB=$([ -n "${{secrets.DOCKER_HUB_USER}}" ] && [ -n "${{secrets.DOCKER_HUB_TOKEN}}" ] && [ -n "${{secrets.DOCKER_HUB_IMAGE}}" ] && echo "true" || echo "false")
|
||||
REGISTRY="$(echo "${{github.server_url}}" | sed -E 's|https?://||')"
|
||||
VERSION=$(cat package.json | grep version | grep -Eo '[0-9][[:alnum:]\.\/\-]+')
|
||||
|
||||
docker login -u "${{github.repository_owner}}" -p "${{secrets.DEPLOY_TOKEN}}" "$REGISTRY"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then docker login -u "${{secrets.DOCKER_HUB_USER}}" -p "${{secrets.DOCKER_HUB_TOKEN}}" docker.io; fi
|
||||
|
||||
docker build -t "$REGISTRY/${{github.repository}}:${{github.ref_name}}" .
|
||||
docker push "$REGISTRY/${{github.repository}}:${{github.ref_name}}"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:${{github.ref_name}}"
|
||||
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:${{github.ref_name}}"
|
||||
fi
|
||||
|
||||
if [ "${{github.ref_name}}" = "master" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "$REGISTRY/${{github.repository}}:$VERSION"
|
||||
docker push "$REGISTRY/${{github.repository}}:$VERSION"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:$VERSION"
|
||||
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:$VERSION"
|
||||
fi
|
||||
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "$REGISTRY/${{github.repository}}:latest"
|
||||
docker push "$REGISTRY/${{github.repository}}:latest"
|
||||
if [ "$DOCKER_HUB" = "true" ]; then
|
||||
docker tag "$REGISTRY/${{github.repository}}:${{github.ref_name}}" "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:latest"
|
||||
docker push "docker.io/${{secrets.DOCKER_HUB_IMAGE}}:latest"
|
||||
fi
|
||||
fi
|
||||
54
.github/workflows/website.yml
vendored
54
.github/workflows/website.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: Build Website
|
||||
run-name: Build Website
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build NPM Project
|
||||
runs-on: ubuntu-latest
|
||||
container: node
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: ztimson/actions/clone@develop
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm i
|
||||
|
||||
- name: Build Project
|
||||
run: npm run build
|
||||
|
||||
- name: Upload Artifacts
|
||||
if: ${{inputs.artifacts}} != "false"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: website
|
||||
path: dist
|
||||
retention-days: 7
|
||||
|
||||
tag:
|
||||
name: Tag Version
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
container: node
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: ztimson/actions/clone@develop
|
||||
|
||||
- name: Get Version Number
|
||||
run: echo "VERSION=$(cat package.json | grep version | grep -Eo ':.+' | grep -Eo '[[:alnum:]\.\/\-]+')" >> $GITHUB_ENV
|
||||
|
||||
- name: Tag Version
|
||||
uses: ztimson/actions/tag@develop
|
||||
with:
|
||||
tag: ${{env.VERSION}}
|
||||
|
||||
publish:
|
||||
name: Build & Push Dockerfile
|
||||
needs: build
|
||||
uses: ztimson/actions/.github/workflows/docker.yaml@develop
|
||||
with:
|
||||
name: ztimson/legio-30
|
||||
repository: ${{github.server_url}}/${{github.repository}}.git
|
||||
pass: ${{secrets.DEPLOY_TOKEN}}
|
||||
12
Dockerfile
12
Dockerfile
@@ -20,11 +20,7 @@ RUN if [ ! -d "dist" ] && [ ! -d "node_modules" ]; then npm install; fi
|
||||
RUN BUILD_MODE=$([ "$NODE_ENV" = "prod" ] && echo "prod" || echo "dev") && \
|
||||
if [ ! -d "dist" ]; then npm run "build:$BUILD_MODE"; fi
|
||||
|
||||
# Use Nginx to serve
|
||||
FROM nginx:1.20-alpine
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY docker/config/robots.txt /usr/share/nginx/html/robots.txt
|
||||
COPY docker/config/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY docker/scripts/setup-environment.sh /docker-entrypoint.d/setup-environment.sh
|
||||
RUN chmod +x /docker-entrypoint.d/setup-environment.sh
|
||||
EXPOSE 80
|
||||
# Use Momentum to serve
|
||||
FROM git.zakscode.com/momentum/momentum
|
||||
RUN rm -rf /app/server/public/assets /app/server/public/index.html
|
||||
COPY --from=build /app/dist /app/server/public
|
||||
|
||||
@@ -53,6 +53,7 @@ Website: https://legio-30.org
|
||||
### Built With
|
||||
[](https://angular.io/)
|
||||
[](https://docker.com/)
|
||||
[](https://momentum.zakscode.com)
|
||||
[](https://typescriptlang.org/)
|
||||
|
||||
## Setup
|
||||
|
||||
13
angular.json
13
angular.json
@@ -33,18 +33,7 @@
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"budgets": [],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
|
||||
@@ -25,7 +25,7 @@ http {
|
||||
autoindex off;
|
||||
|
||||
location / {
|
||||
try_files $uri$args $uri$args/ /index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
package-lock.json
generated
52
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "legio-xxx",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "legio-xxx",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.2.0",
|
||||
"@angular/cdk": "^14.2.2",
|
||||
@@ -18,6 +18,7 @@
|
||||
"@angular/platform-browser": "^14.2.0",
|
||||
"@angular/platform-browser-dynamic": "^14.2.0",
|
||||
"@angular/router": "^14.2.0",
|
||||
"@ztimson/momentum": "^1.2.1",
|
||||
"bootstrap": "^5.2.1",
|
||||
"jquery": "^3.6.1",
|
||||
"ngx-google-analytics": "^14.0.1",
|
||||
@@ -3340,6 +3341,26 @@
|
||||
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ztimson/momentum": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ztimson/momentum/-/momentum-1.2.1.tgz",
|
||||
"integrity": "sha512-O3Z06SfMin6zXlW0jZjgS16qwcyPtKfJ8hRzV6SuFlN9Il8y7HepUylnV8Hd5RTdWXeztBmo+pNTD/RjPJ37wQ==",
|
||||
"dependencies": {
|
||||
"@ztimson/utils": "0.29.1"
|
||||
},
|
||||
"bin": {
|
||||
"build-models": "bin/build-models.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@ztimson/utils": {
|
||||
"version": "0.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.29.1.tgz",
|
||||
"integrity": "sha512-tc1Eikjt7bwMHoOvck5eSnV3P69FOVNp6z7n/v1IoEtBmHLtPgz8LsxUtSYLnvl96HZ1bNkvQfuuUxrBob5dJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"var-persist": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||
@@ -11205,6 +11226,12 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/var-persist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/var-persist/-/var-persist-1.0.1.tgz",
|
||||
"integrity": "sha512-Zon+pwvEpb0dEQCVShoMQQWV1JbWi4P2knW3h2sfSZS3pLecgbFig76tMSHEECQuEQ3KfYhMXgRDDIybtHTyZw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@@ -14070,6 +14097,22 @@
|
||||
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@ztimson/momentum": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ztimson/momentum/-/momentum-1.2.1.tgz",
|
||||
"integrity": "sha512-O3Z06SfMin6zXlW0jZjgS16qwcyPtKfJ8hRzV6SuFlN9Il8y7HepUylnV8Hd5RTdWXeztBmo+pNTD/RjPJ37wQ==",
|
||||
"requires": {
|
||||
"@ztimson/utils": "0.29.1"
|
||||
}
|
||||
},
|
||||
"@ztimson/utils": {
|
||||
"version": "0.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@ztimson/utils/-/utils-0.29.1.tgz",
|
||||
"integrity": "sha512-tc1Eikjt7bwMHoOvck5eSnV3P69FOVNp6z7n/v1IoEtBmHLtPgz8LsxUtSYLnvl96HZ1bNkvQfuuUxrBob5dJQ==",
|
||||
"requires": {
|
||||
"var-persist": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||
@@ -19750,6 +19793,11 @@
|
||||
"builtins": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"var-persist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/var-persist/-/var-persist-1.0.1.tgz",
|
||||
"integrity": "sha512-Zon+pwvEpb0dEQCVShoMQQWV1JbWi4P2knW3h2sfSZS3pLecgbFig76tMSHEECQuEQ3KfYhMXgRDDIybtHTyZw=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "legio-xxx",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
@@ -21,6 +21,7 @@
|
||||
"@angular/platform-browser": "^14.2.0",
|
||||
"@angular/platform-browser-dynamic": "^14.2.0",
|
||||
"@angular/router": "^14.2.0",
|
||||
"@ztimson/momentum": "^1.2.1",
|
||||
"bootstrap": "^5.2.1",
|
||||
"jquery": "^3.6.1",
|
||||
"ngx-google-analytics": "^14.0.1",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {NgxGoogleAnalyticsModule} from 'ngx-google-analytics';
|
||||
import {environment} from '../environments/environment';
|
||||
@@ -14,6 +14,7 @@ import {NavbarComponent} from './components/navbar/navbar.component';
|
||||
import {PlaceholderComponent} from './components/placeholder/placeholder.component';
|
||||
import {AppComponent} from './containers/app/app.component';
|
||||
import {BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import {InViewDirective} from './directives/in-view.directive';
|
||||
import {MaterialModule} from './material.module';
|
||||
import {PrelaodService} from './services/prelaod.service';
|
||||
import {FourOFourComponent} from './views/404/404.component';
|
||||
@@ -24,13 +25,14 @@ import {CalendarComponent} from './views/events/calendar/calendar.component';
|
||||
import {HibernaComponent} from './views/events/hiberna/hiberna.component';
|
||||
import {GalleryComponent} from './views/gallery/gallery.component';
|
||||
import {HomeComponent} from './views/home/home.component';
|
||||
import {DiyComponent} from './views/reenact/diy/diy.component';
|
||||
import {DrillComponent} from './views/reenact/drill/drill.component';
|
||||
import {EquipmentComponent} from './views/reenact/equipment/equipment.component';
|
||||
import {GettingStartedComponent} from './views/reenact/getting-started/getting-started.component';
|
||||
import {MaintenanceComponent} from './views/reenact/maintinance/maintenance.component';
|
||||
import {RulesComponent} from './views/reenact/rules/rules.component';
|
||||
import {BuyComponent} from './views/reenact/buy/buy.component';
|
||||
import {ResourcesComponent} from './views/resources/resources.component';
|
||||
import {RegisterComponent} from './views/register/register.component';
|
||||
import {UploaderComponent} from './components/uploader/uploader.component';
|
||||
|
||||
export const APP_COMPONENTS: any[] = [
|
||||
AboutComponent,
|
||||
@@ -38,12 +40,11 @@ export const APP_COMPONENTS: any[] = [
|
||||
AgoniumComponent,
|
||||
AppComponent,
|
||||
BannerComponent,
|
||||
BuyComponent,
|
||||
CalendarComponent,
|
||||
CarouselComponent,
|
||||
ContactComponent,
|
||||
DiyComponent,
|
||||
DrillComponent,
|
||||
EquipmentComponent,
|
||||
FooterComponent,
|
||||
FourOFourComponent,
|
||||
GalleryComponent,
|
||||
@@ -51,18 +52,22 @@ export const APP_COMPONENTS: any[] = [
|
||||
HibernaComponent,
|
||||
HomeComponent,
|
||||
ImageViewerComponent,
|
||||
InViewDirective,
|
||||
LogoComponent,
|
||||
MaintenanceComponent,
|
||||
NavbarComponent,
|
||||
PlaceholderComponent,
|
||||
RegisterComponent,
|
||||
ResourcesComponent,
|
||||
RulesComponent,
|
||||
UploaderComponent,
|
||||
]
|
||||
|
||||
export const APP_IMPORTS: any[] = [
|
||||
AppRouting,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MaterialModule
|
||||
]
|
||||
|
||||
@@ -8,20 +8,19 @@ import {CalendarComponent} from './views/events/calendar/calendar.component';
|
||||
import {HibernaComponent} from './views/events/hiberna/hiberna.component';
|
||||
import {GalleryComponent} from './views/gallery/gallery.component';
|
||||
import {HomeComponent} from './views/home/home.component';
|
||||
import {DiyComponent} from './views/reenact/diy/diy.component';
|
||||
import {DrillComponent} from './views/reenact/drill/drill.component';
|
||||
import {EquipmentComponent} from './views/reenact/equipment/equipment.component';
|
||||
import {GettingStartedComponent} from './views/reenact/getting-started/getting-started.component';
|
||||
import {MaintenanceComponent} from './views/reenact/maintinance/maintenance.component';
|
||||
import {RulesComponent} from './views/reenact/rules/rules.component';
|
||||
import {BuyComponent} from './views/reenact/buy/buy.component';
|
||||
import {RegisterComponent} from './views/register/register.component';
|
||||
import {ResourcesComponent} from './views/resources/resources.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', pathMatch: 'full', component: HomeComponent},
|
||||
{path: 'about', component: AboutComponent, data: {title: 'About'}},
|
||||
{path: 'buy', component: BuyComponent, data: {title: 'Buy'}},
|
||||
{path: 'drill', component: DrillComponent, data: {title: 'Drill Commands'}},
|
||||
{path: 'diy', component: DiyComponent, data: {title: 'Build Equipment'}},
|
||||
{path: 'equipment', component: EquipmentComponent, data: {title: 'Equipment'}},
|
||||
{path: 'events/agonium', component: AgoniumComponent, data: {title: 'Agonium'}},
|
||||
{path: 'events/castra-aestiva', component: AestivaComponent, data: {title: 'Castra Aestiva'}},
|
||||
{path: 'events/castra-hiberna', component: HibernaComponent, data: {title: 'Castra Hiberna'}},
|
||||
@@ -30,6 +29,7 @@ const routes: Routes = [
|
||||
{path: 'getting-started', component: GettingStartedComponent, data: {title: 'Getting Started'}},
|
||||
{path: 'info/resources', component: ResourcesComponent, data: {title: 'Resources'}},
|
||||
{path: 'maintenance', component: MaintenanceComponent, data: {title: 'Maintenance'}},
|
||||
{path: 'register', component: RegisterComponent, data: {title: 'Register'}},
|
||||
{path: 'rules', component: RulesComponent, data: {title: 'Rules & Regulations'}},
|
||||
{path: '**', component: FourOFourComponent, data: {title: '404'}}
|
||||
];
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
<mat-icon *ngIf="manual">play_arrow</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="!manual" class="banner-seal d-flex flex-column align-items-center justify-content-center">
|
||||
<img src="/assets/img/favicon.svg" class="mt-5" alt="SPQR" height="250" width="250" style="filter: brightness(100%) drop-shadow(2px 4px 6px black);">
|
||||
<img src="/assets/img/favicon.svg" class="mt-5" alt="SPQR" height="250" width="250">
|
||||
<div>
|
||||
<a class="text-white" routerLink="" fragment="about">
|
||||
<i class="fa fa-angle-double-down fa-4x" style="filter: drop-shadow(2px 4px 6px black);"></i>
|
||||
</a>
|
||||
<button type="button" class="banner-scroll text-white" (click)="scrollToAbout()" aria-label="Scroll to about section">
|
||||
<i class="fa fa-angle-double-down fa-4x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,48 +3,192 @@
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 420px;
|
||||
background: #111;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.banner-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(0, 0, 0, 0.32), rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.32)),
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.34));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.banner-container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: auto 0 0;
|
||||
z-index: 2;
|
||||
height: 28%;
|
||||
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.38));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.banner-background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: blur(10px);
|
||||
-webkit-filter: blur(10px);
|
||||
object-fit: cover;
|
||||
transform: scale(1.04);
|
||||
filter: blur(8px) brightness(90%) saturate(112%);
|
||||
-webkit-filter: blur(8px) brightness(90%) saturate(112%);
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
height: 90%;
|
||||
width: auto;
|
||||
max-width: 92%;
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
object-fit: contain;
|
||||
border-radius: 14px;
|
||||
box-shadow:
|
||||
0 18px 44px rgba(0, 0, 0, 0.36),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.14);
|
||||
filter: contrast(102%) saturate(104%);
|
||||
}
|
||||
|
||||
.banner-next,
|
||||
.banner-previous,
|
||||
.banner-pause {
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
border-radius: 999px;
|
||||
background: rgba(0, 0, 0, 0.34);
|
||||
border: 1px solid rgba(255, 255, 255, 0.24);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.24);
|
||||
transition:
|
||||
background 160ms ease,
|
||||
border-color 160ms ease,
|
||||
box-shadow 160ms ease,
|
||||
color 160ms ease;
|
||||
}
|
||||
|
||||
.banner-next:hover,
|
||||
.banner-previous:hover,
|
||||
.banner-pause:hover {
|
||||
background: rgba(75, 15, 15, 0.55);
|
||||
border-color: rgba(240, 195, 106, 0.52);
|
||||
color: #f0c36a;
|
||||
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
.banner-next mat-icon,
|
||||
.banner-previous mat-icon,
|
||||
.banner-pause mat-icon {
|
||||
font-size: 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.42));
|
||||
}
|
||||
|
||||
.banner-next {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 26px;
|
||||
transform: translate(-50%, -50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.banner-previous {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50px;
|
||||
transform: translate(-50%, -50%);
|
||||
left: 26px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.banner-seal {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.banner-seal img {
|
||||
max-width: min(250px, 42vw);
|
||||
height: auto;
|
||||
opacity: 0.95;
|
||||
filter: brightness(108%) drop-shadow(0 10px 16px rgba(0, 0, 0, 0.65)) !important;
|
||||
}
|
||||
|
||||
.banner-seal a,
|
||||
.banner-scroll {
|
||||
pointer-events: auto;
|
||||
display: inline-flex;
|
||||
margin-top: 18px;
|
||||
color: #fff;
|
||||
opacity: 0.9;
|
||||
transition:
|
||||
opacity 160ms ease,
|
||||
color 160ms ease;
|
||||
}
|
||||
|
||||
.banner-scroll {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
filter: drop-shadow(2px 4px 6px black);
|
||||
}
|
||||
|
||||
.banner-seal a:hover,
|
||||
.banner-scroll:hover {
|
||||
color: #f0c36a !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.banner-pause {
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform: translateX(-50%);
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.banner-container {
|
||||
min-height: 340px;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
height: 78%;
|
||||
max-width: 96%;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.banner-next,
|
||||
.banner-previous {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.banner-next {
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.banner-previous {
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.banner-pause {
|
||||
bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class BannerComponent implements AfterViewInit, OnDestroy, OnInit {
|
||||
this.sub = interval(this.speed)
|
||||
.subscribe( i => {
|
||||
if(this.manual) return;
|
||||
this.selected = i % this.images.length
|
||||
this.selected = i % this.images.length
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,4 +66,11 @@ export class BannerComponent implements AfterViewInit, OnDestroy, OnInit {
|
||||
this.selected--;
|
||||
if(this.selected < 0) this.selected = this.images.length - 1;
|
||||
}
|
||||
|
||||
scrollToAbout() {
|
||||
document.getElementById('about')?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {AfterViewInit, Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
||||
import {Photo} from '../models/photo';
|
||||
|
||||
@Component({
|
||||
@@ -18,6 +18,7 @@ export class CarouselComponent implements OnDestroy, OnInit, AfterViewInit {
|
||||
@Input() disableAutoplay = false;
|
||||
@Input() index = ~~(Math.random() * this.photos.length);
|
||||
@Input() height = '100%';
|
||||
@Output() indexChange = new EventEmitter<number>();
|
||||
|
||||
ngOnInit() {
|
||||
if(this.disableAutoplay) this.pause = true;
|
||||
@@ -41,11 +42,13 @@ export class CarouselComponent implements OnDestroy, OnInit, AfterViewInit {
|
||||
this.pause = pause;
|
||||
this.index++;
|
||||
if(this.index >= this.photos.length) this.index = 0;
|
||||
this.indexChange.emit(this.index);
|
||||
}
|
||||
|
||||
previous(pause = true) {
|
||||
this.loading = true;
|
||||
this.pause = pause;
|
||||
this.index = this.index > 0 ? this.index - 1 : this.photos.length - 1;
|
||||
this.indexChange.emit(this.index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
<div class="d-flex flex-column-reverse flex-md-row justify-content-center cap-width">
|
||||
<div style="flex: 2 0 0;">
|
||||
<div *ngIf="error" class="alert alert-danger py-2">
|
||||
Coming Soon: This feature is under development
|
||||
{{error}}
|
||||
</div>
|
||||
<form [formGroup]="form">
|
||||
<div *ngIf="success" class="alert alert-success py-2">
|
||||
Your message was sent successfully.
|
||||
</div>
|
||||
<form [formGroup]="form" (ngSubmit)="submitEmail()">
|
||||
<div>
|
||||
<mat-form-field appearance="fill" class="w-100">
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field appearance="fill" class="w-100">
|
||||
<mat-label>Email</mat-label>
|
||||
@@ -25,13 +34,10 @@
|
||||
<textarea matInput rows="10" formControlName="body"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex justify-content-end align-items-center">
|
||||
<div>
|
||||
<mat-checkbox color="primary">Send me a copy</mat-checkbox>
|
||||
</div>
|
||||
<div>
|
||||
<button mat-stroked-button class="me-3" (click)="reset()">Reset</button>
|
||||
<button mat-raised-button color="primary" (click)="error = true">Send</button>
|
||||
<button mat-stroked-button type="button" class="me-3" (click)="reset()">Reset</button>
|
||||
<button mat-raised-button type="submit" color="primary" [disabled]="success">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -41,17 +47,24 @@
|
||||
<div class="d-none d-md-block mx-4 border-end border-dark" style="height: 100%; width: 1px;"></div>
|
||||
</div>
|
||||
<div class="d-flex flex-column text-center text-md-start align-self-center" style="width: min(100%, 250px)">
|
||||
<div *ngIf="form.controls['subject'].value != 'Castra'">
|
||||
<h3>Robert Sacco</h3>
|
||||
<h4 class="mb-0">Legio XXX President</h4>
|
||||
<h5>Portrays: Aquilifer Primus Marius Maximus</h5>
|
||||
<a href="mailto:primuspiluslxxx@gmail.com" target="_blank">primuspiluslxxx@gmail.com</a>
|
||||
</div>
|
||||
<div *ngIf="form.controls['subject'].value == 'Castra'">
|
||||
<h3>Tom Ross</h3>
|
||||
<h4 class="mb-0">Legio XXX <em>Patronus</em> (Patron)</h4>
|
||||
<h5>Portrays: Titus Quartinius Saturnalus</h5>
|
||||
<a href="mailto:tomlongwoods@gmail.com" target="_blank">tomlongwoods@gmail.com</a>
|
||||
<div class="d-flex flex-column text-center text-md-start align-self-center" style="width: min(100%, 250px)">
|
||||
<div
|
||||
class="mb-3"
|
||||
[ngClass]="form.controls['subject'].value != 'Castra' ? '' : 'opacity-50'"
|
||||
>
|
||||
<h3>Robert Sacco</h3>
|
||||
<h4 class="mb-0">Legio XXX President</h4>
|
||||
<h5>Portrays: Aquilifer Primus Marius Maximus</h5>
|
||||
<a href="mailto:primuspiluslxxx@gmail.com" target="_blank">primuspiluslxxx@gmail.com</a>
|
||||
</div>
|
||||
<div
|
||||
[ngClass]="form.controls['subject'].value == 'Castra' ? '' : 'opacity-50'"
|
||||
>
|
||||
<h3>Tom Ross</h3>
|
||||
<h4 class="mb-0">Legio XXX <em>Patronus</em> (Patron)</h4>
|
||||
<h5>Portrays: Titus Quartinius Saturnalus</h5>
|
||||
<a href="mailto:tomlongwoods@gmail.com" target="_blank">tomlongwoods@gmail.com</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {FormBuilder, FormGroup} from '@angular/forms';
|
||||
import {MomentumService} from '../../services/momentum.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact',
|
||||
@@ -7,18 +8,34 @@ import {FormBuilder, FormGroup} from '@angular/forms';
|
||||
})
|
||||
export class ContactComponent {
|
||||
public error = false;
|
||||
public success = false;
|
||||
public form!: FormGroup;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
constructor(private fb: FormBuilder, private momentum: MomentumService) {
|
||||
this.form = fb.group({
|
||||
name: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
});
|
||||
}
|
||||
|
||||
public async submitEmail(): Promise<void> {
|
||||
this.error = false;
|
||||
this.success = false;
|
||||
|
||||
try {
|
||||
await this.momentum.api.data.create(MomentumService.SCHEMA['contact'], this.form.value);
|
||||
this.success = true;
|
||||
this.form.disable();
|
||||
} catch (error: any) {
|
||||
this.error = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.error = false;
|
||||
this.success = false;
|
||||
this.form.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
<footer>
|
||||
<div class="social text-center py-3" style="background: #990000">
|
||||
<h2 class="mb-4">Follow us on social media</h2>
|
||||
<div class="d-flex justify-content-around mx-auto transparent-link" style="max-width: 300px">
|
||||
<a href="https://discord.gg/wW458KYR79" target="_blank">
|
||||
<i class="fa-brands fa-discord fa-2xl"></i>
|
||||
</a>
|
||||
<a href="https://facebook.com" target="_blank" aria-label="Facebook">
|
||||
<i class="fa-brands fa-facebook fa-2xl"></i>
|
||||
</a>
|
||||
<a href="https://instagram.com" target="_blank" aria-label="Instagram">
|
||||
<i class="fa-brands fa-instagram fa-2xl"></i>
|
||||
</a>
|
||||
<a href="https://tiktok.com" target="_blank" aria-label="TikTok">
|
||||
<i class="fa-brands fa-tiktok fa-2xl"></i>
|
||||
</a>
|
||||
<a href="https://youtube.com" target="_blank" aria-label="Youtube">
|
||||
<i class="fa-brands fa-youtube fa-2xl"></i>
|
||||
</a>
|
||||
<div class="text-center d-flex flex-row justify-content-center align-items-center gap-3">
|
||||
<hr class="flex-grow-1 m-0 opacity-75">
|
||||
<div class="d-flex flex-column align-items-end">
|
||||
<h2 class="mb-0">Ready to Enlist?</h2>
|
||||
<p class="mb-0">Take your place in our ranks...</p>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<a [routerLink]="['/register']" class="btn btn-light btn-lg">JOIN NOW</a>
|
||||
</div>
|
||||
<hr class="flex-grow-1 m-0 opacity-75">
|
||||
</div>
|
||||
<h3 class="mt-4 mb-0">so we can invade your feed ⚔️</h3>
|
||||
</div>
|
||||
<div class="bg-dark text-center text-sm-start">
|
||||
<div class="d-flex flex-column flex-sm-row flex-wrap justify-content-center container p-3 pb-0">
|
||||
@@ -45,7 +37,7 @@
|
||||
<div class="py-3 text-center">
|
||||
<p class="copyright m-0">
|
||||
Copyright © Legio XXX 2024 | All Rights Reserved<br>
|
||||
Created by <a href="https://zakscode.com" target="_blank">Zak Timson</a>
|
||||
Created by <a href="https://zakscode.com" target="_blank">Zak Timson</a> | Built with <a href="https://momentum.zakscode.com" target="_blank">Momentum</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {MomentumService} from '../../services/momentum.service';
|
||||
import {Photo} from '../models/photo';
|
||||
|
||||
export interface ImageViewerOptions {
|
||||
@@ -10,12 +11,13 @@ export interface ImageViewerOptions {
|
||||
@Component({
|
||||
selector: 'xxx-viewer',
|
||||
styles: [`
|
||||
.close {
|
||||
.close, .delete {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 100000;
|
||||
}
|
||||
.close { right: 1rem; }
|
||||
.delete { right: 4rem; }
|
||||
::ng-deep .cdk-overlay-pane {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
@@ -24,18 +26,32 @@ export interface ImageViewerOptions {
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
<button class="close" mat-icon-button aria-label="close dialog" mat-dialog-close>
|
||||
<button class="close" mat-icon-button mat-dialog-close>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<xxx-carousel [photos]="photos" [index]="index" [disableAutoplay]="true"></xxx-carousel>
|
||||
<button *ngIf="momentum.admin | async" class="delete me-3" mat-icon-button (click)="delete()">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
<xxx-carousel [photos]="photos" [index]="index" [disableAutoplay]="true" (indexChange)="index = $event"></xxx-carousel>
|
||||
`
|
||||
})
|
||||
export class ImageViewerComponent {
|
||||
index!: number;
|
||||
photos!: Photo[];
|
||||
|
||||
constructor(public ref: MatDialogRef<ImageViewerComponent>, @Inject(MAT_DIALOG_DATA) data: ImageViewerOptions) {
|
||||
constructor(
|
||||
public ref: MatDialogRef<ImageViewerComponent>,
|
||||
public momentum: MomentumService,
|
||||
@Inject(MAT_DIALOG_DATA) data: ImageViewerOptions
|
||||
) {
|
||||
this.index = data.index || 0;
|
||||
this.photos = data.photos || [];
|
||||
}
|
||||
|
||||
async delete() {
|
||||
if (!confirm('Delete this photo?')) return;
|
||||
const photo = this.photos[this.index];
|
||||
await this.momentum.api.storage.delete(photo.path);
|
||||
this.ref.close({deleted: photo});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface Photo {
|
||||
alt: string;
|
||||
src: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
@@ -18,19 +18,43 @@
|
||||
<mat-menu #menu="matMenu">
|
||||
<ng-container *ngFor="let section of group.children; let first = first">
|
||||
<mat-divider *ngIf="!first"></mat-divider>
|
||||
<button *ngFor="let item of section" mat-menu-item [routerLink]="item.url" [fragment]="item.fragment">
|
||||
<button *ngFor="let item of section" mat-menu-item (click)="openItem(item)">
|
||||
{{item.label}}
|
||||
</button>
|
||||
</ng-container>
|
||||
<!-- Auth items, Members menu only -->
|
||||
<ng-container *ngIf="group.label === 'Members'">
|
||||
<mat-divider></mat-divider>
|
||||
<ng-container *ngIf="momentum.isLoggedIn | async; else guestItems">
|
||||
<button mat-menu-item (click)="momentum.api.auth.logout()">Logout</button>
|
||||
</ng-container>
|
||||
<ng-template #guestItems>
|
||||
<button mat-menu-item (click)="momentum.api.auth.handleLogin()">Login</button>
|
||||
<button mat-menu-item (click)="openItem({label:'Register', url:'/register'})">Register</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
<button mat-button [matMenuTriggerFor]="menu2" class="navbar-button">
|
||||
Members <mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu2="matMenu">
|
||||
<ng-container *ngIf="momentum.isLoggedIn | async; else guestItems">
|
||||
<button *ngIf="momentum.admin | async" mat-menu-item (click)="openAdmin()">Admin</button>
|
||||
<button mat-menu-item (click)="momentum.api.auth.logout()">Logout</button>
|
||||
</ng-container>
|
||||
<ng-template #guestItems>
|
||||
<button mat-menu-item (click)="momentum.api.auth.handleLogin()">Login</button>
|
||||
<button mat-menu-item (click)="openItem({label:'Register', url:'/register'})">Register</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<!-- Mobile NavBar -->
|
||||
<!-- Mobile NavBar-->
|
||||
<button *ngIf="hamburger" mat-icon-button class="text-start" [matMenuTriggerFor]="menu">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item *ngFor="let item of links.topLevel" [routerLink]="item.url" [fragment]="item.fragment">
|
||||
<button mat-menu-item *ngFor="let item of links.topLevel" (click)="openItem(item)">
|
||||
{{item.label}}
|
||||
</button>
|
||||
<ng-container *ngFor="let group of links.other">
|
||||
@@ -44,6 +68,19 @@
|
||||
</ng-container>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
<button mat-menu-item [matMenuTriggerFor]="menu3" class="navbar-button">
|
||||
Members
|
||||
</button>
|
||||
<mat-menu #menu3="matMenu">
|
||||
<ng-container *ngIf="momentum.isLoggedIn | async; else guestItems">
|
||||
<button *ngIf="momentum.admin | async" mat-menu-item (click)="openAdmin()">Admin</button>
|
||||
<button mat-menu-item (click)="momentum.api.auth.logout()">Logout</button>
|
||||
</ng-container>
|
||||
<ng-template #guestItems>
|
||||
<button mat-menu-item (click)="momentum.api.auth.handleLogin()">Login</button>
|
||||
<button mat-menu-item (click)="openItem({label:'Register', url:'/register'})">Register</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
</mat-menu>
|
||||
</mat-toolbar-row>
|
||||
</mat-toolbar>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {AfterViewInit, Component, EventEmitter, Input, OnDestroy, Output} from '@angular/core';
|
||||
import {ActivatedRoute, NavigationEnd, NavigationStart, Router} from '@angular/router';
|
||||
import {ActivatedRoute, NavigationStart, Router} from '@angular/router';
|
||||
import {combineLatest, filter, Subscription} from 'rxjs';
|
||||
import {NAVIGATION} from '../../misc/navigation';
|
||||
import {BreakpointService} from '../../services/breakpoint.service';
|
||||
import {NAVIGATION, NavigationItem} from '../../misc/navigation';
|
||||
import {MomentumService} from '../../services/momentum.service';
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-navbar',
|
||||
@@ -21,7 +21,7 @@ export class NavbarComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
@Output() hamburgerClick = new EventEmitter<void>();
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router, public breakpoint: BreakpointService) { }
|
||||
constructor(private route: ActivatedRoute, private router: Router, public momentum: MomentumService) { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.sub = combineLatest([this.router.events.pipe(filter((e: any) => e.navigationTrigger != 'popstate' || e instanceof NavigationStart)), this.route.fragment]).subscribe(([url, frag]) => {
|
||||
@@ -34,6 +34,19 @@ export class NavbarComponent implements AfterViewInit, OnDestroy {
|
||||
if(this.sub) this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
openAdmin() {
|
||||
location.href = '/ui';
|
||||
}
|
||||
|
||||
openItem(item: NavigationItem) {
|
||||
// Full url
|
||||
if(item.url.startsWith('http'))
|
||||
location.href = item.url;
|
||||
// Relative
|
||||
else
|
||||
this.router.navigate([item.url], {fragment: item.fragment});
|
||||
}
|
||||
|
||||
scroll(id: string) {
|
||||
const el = document.getElementById(id);
|
||||
if(el) el.scrollIntoView({behavior: 'smooth'});
|
||||
|
||||
16
src/app/components/uploader/uploader.component.html
Normal file
16
src/app/components/uploader/uploader.component.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<h2 mat-dialog-title>Upload Photos</h2>
|
||||
<mat-dialog-content>
|
||||
<p class="text-muted">Select the year these photos were taken then choose your files.</p>
|
||||
<mat-form-field appearance="outline" class="w-100">
|
||||
<mat-label>Year</mat-label>
|
||||
<mat-select [(ngModel)]="year">
|
||||
<mat-option *ngFor="let y of years" [value]="y">{{y}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button mat-raised-button color="primary" (click)="upload()" [disabled]="uploading">
|
||||
<mat-icon>upload</mat-icon> Choose Files
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
21
src/app/components/uploader/uploader.component.ts
Normal file
21
src/app/components/uploader/uploader.component.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {MatDialogRef} from '@angular/material/dialog';
|
||||
import {MomentumService} from '../../services/momentum.service';
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-uploader',
|
||||
templateUrl: './uploader.component.html',
|
||||
})
|
||||
export class UploaderComponent {
|
||||
year: number = new Date().getFullYear();
|
||||
years: number[] = Array.from({length: this.year - 2006}, (_, i) => 2007 + i).reverse();
|
||||
uploading = false;
|
||||
|
||||
constructor(private momentum: MomentumService, private ref: MatDialogRef<UploaderComponent>) {}
|
||||
|
||||
async upload() {
|
||||
this.uploading = true;
|
||||
await this.momentum.api.storage.upload(`Photos/Submissions/${this.year}`, undefined, {multiple: true, accept: 'image/*'});
|
||||
this.ref.close();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Component } from '@angular/core';
|
||||
import {NavigationEnd, Router} from '@angular/router';
|
||||
import {combineLatest, filter, Subscription} from 'rxjs';
|
||||
import {BreakpointService} from '../../services/breakpoint.service';
|
||||
import {MomentumService} from '../../services/momentum.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -14,7 +15,7 @@ export class AppComponent {
|
||||
mobile = false;
|
||||
open = false;
|
||||
|
||||
constructor(private breakpoint: BreakpointService, private router: Router) {
|
||||
constructor(private breakpoint: BreakpointService, private momentum: MomentumService, private router: Router) {
|
||||
this.sub = combineLatest([
|
||||
router.events.pipe(filter(event => event instanceof NavigationEnd)),
|
||||
breakpoint.isMobile$
|
||||
@@ -24,6 +25,10 @@ export class AppComponent {
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.momentum.api.client.inject();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if(this.sub) this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
24
src/app/directives/in-view.directive.ts
Normal file
24
src/app/directives/in-view.directive.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {Directive, ElementRef, EventEmitter, OnDestroy, OnInit, Output} from '@angular/core';
|
||||
|
||||
@Directive({selector: '[inView]'})
|
||||
export class InViewDirective implements OnInit, OnDestroy {
|
||||
@Output() inView = new EventEmitter<void>();
|
||||
private observer!: IntersectionObserver;
|
||||
|
||||
constructor(private el: ElementRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.observer = new IntersectionObserver(([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
this.inView.emit();
|
||||
this.observer.disconnect();
|
||||
}
|
||||
}, {rootMargin: '200px'});
|
||||
|
||||
this.observer.observe(this.el.nativeElement);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,31 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatNativeDateModule} from '@angular/material/core';
|
||||
import {MatDatepickerModule} from '@angular/material/datepicker';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {MatDividerModule} from '@angular/material/divider';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
|
||||
export const MATERIAL_MODULES = [
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
MatMenuModule,
|
||||
MatNativeDateModule,
|
||||
MatSelectModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatToolbarModule
|
||||
];
|
||||
|
||||
|
||||
@@ -14,13 +14,6 @@ export const NAVIGATION: NavigationGroup = [
|
||||
{label: 'About', url: '/about'},
|
||||
{label: 'Contact', url: '/', fragment: 'contact'},
|
||||
{label: 'Gallery', url: '/gallery'},
|
||||
{label: 'Resources', url: '/info/resources'},
|
||||
]]},
|
||||
{label: 'Events', children: [[
|
||||
{label: 'Castra Aestiva', url: '/events/castra-aestiva'},
|
||||
{label: 'Castra Hiberna', url: '/events/castra-hiberna'},
|
||||
], [
|
||||
{label: 'Calendar', url: '/events/calendar'},
|
||||
]]},
|
||||
// {label: 'Learn', children: [[
|
||||
// {label: 'Trajan', url: '/info/trajan'},
|
||||
@@ -35,14 +28,21 @@ export const NAVIGATION: NavigationGroup = [
|
||||
// {label: 'Glossary', url: '/info/glossary'},
|
||||
// {label: 'Resources', url: '/info/resources'},
|
||||
// ]]},
|
||||
{label: 'Reenact', children: [[
|
||||
{label: 'Reenact', children: [
|
||||
[
|
||||
{label: 'Getting Started', url: '/getting-started'},
|
||||
{label: 'Rules & Regulations', url: '/rules'},
|
||||
{label: 'Drill Commands', url: '/drill'},
|
||||
], [
|
||||
{label: 'Buy Equipment', url: '/buy'},
|
||||
{label: 'Build Equipment', url: '/diy'},
|
||||
{label: 'Equipment', url: '/equipment'},
|
||||
{label: 'Maintenance', url: '/maintenance'},
|
||||
{label: 'Resources', url: '/info/resources'},
|
||||
{label: 'Rules & Regulations', url: '/rules'},
|
||||
]
|
||||
]},
|
||||
{label: 'Events', children: [[
|
||||
{label: 'Calendar', url: '/events/calendar'},
|
||||
], [
|
||||
{label: 'Castra Aestiva', url: '/events/castra-aestiva'},
|
||||
{label: 'Castra Hiberna', url: '/events/castra-hiberna'},
|
||||
]
|
||||
]},
|
||||
]
|
||||
|
||||
54
src/app/services/momentum.service.ts
Normal file
54
src/app/services/momentum.service.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Momentum} from '@ztimson/momentum';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
import {from, map} from 'rxjs';
|
||||
import {filter} from 'rxjs/operators';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
momentum: Momentum;
|
||||
electronEnvironment: any;
|
||||
}
|
||||
|
||||
interface location {
|
||||
port: string;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class MomentumService {
|
||||
static SCHEMA: {[key: string]: string} = {
|
||||
contact: 'Contact',
|
||||
}
|
||||
|
||||
api!: Momentum;
|
||||
|
||||
installable = new BehaviorSubject(false);
|
||||
settings = new BehaviorSubject<{[key: string]: any}>({});
|
||||
title = new BehaviorSubject<string | null>(null);
|
||||
|
||||
// @ts-ignore
|
||||
user = new BehaviorSubject<User | null | undefined>(undefined); // Undefined at init, null when logged out, object when logged in.
|
||||
admin = from(this.user).pipe(filter(u => u !== undefined), map(u => this.api.permissions.has('admin')));
|
||||
isLoggedIn = from(this.user).pipe(filter(u => u !== undefined), map(Boolean));
|
||||
|
||||
constructor() {
|
||||
this.api = window['momentum'] = new Momentum("https://legio-30.org", {
|
||||
app: "Website",
|
||||
analytics: "prompt",
|
||||
logLevel: "ERROR",
|
||||
persist: true,
|
||||
});
|
||||
|
||||
this.api.auth.on('login', () => this.user.next(this.api.auth.user));
|
||||
this.api.auth.on('logout', () => location.reload());
|
||||
this.api.client.on('install', () => this.installable.next(this.api.client.canInstall));
|
||||
this.installable.next(this.api.client.canInstall);
|
||||
|
||||
this.api.auth.readSession();
|
||||
this.api.settings.sync((event, value) => {
|
||||
this.title.next(value['title']);
|
||||
this.settings.next(value);
|
||||
}, {map: true});
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,13 @@
|
||||
<h4 class="mb-2">Museums</h4>
|
||||
<ul class="mt-0">
|
||||
<li>Canadian Museum of History (formerly the Museum of Civilization), Gatineau PQ (2005)</li>
|
||||
<li>Royal Ontario Museum, Toronto ON (2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2023)</li>
|
||||
<li>Royal Ontario Museum, Toronto ON (2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2023, 2024)</li>
|
||||
<li>RCR Museum, Wolseley Barracks, London ON (2019)</li>
|
||||
<li>Ontario Regiment RCAC Museum, Whitby, ON (2023)</li>
|
||||
<li>Ontario Regiment RCAC Museum, Whitby, ON</li>
|
||||
<ul>
|
||||
<li>Aquino Weekend (2023, 2025)</li>
|
||||
<li>The Evolution of Soldiers and Vehicles (2024)</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mx-3">
|
||||
@@ -44,8 +48,8 @@
|
||||
<li>Montessori School, Hamilton ON (2012)</li>
|
||||
<li>Our Lady of Mount Carmel Academy (2016)</li>
|
||||
<li>John Chrysostom Catholic Elementary School (2017)</li>
|
||||
<li>Saunders Secondary School, London ON (2017, 2019, 2022, 2023)</li>
|
||||
<li>Toronto Waldorf School, Thornhill ON (2023)</li>
|
||||
<li>Saunders Secondary School, London ON (2017, 2019, 2022, 2023, 2024, 2025)</li>
|
||||
<li>Toronto Waldorf School, Thornhill ON (2023, 2024)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mx-3">
|
||||
@@ -53,7 +57,7 @@
|
||||
<ul class="mt-0">
|
||||
<li>Toronto Living History Conference, Toronto ON (2007)</li>
|
||||
<li>Thorold Italian Festival, Thorold ON (2008)</li>
|
||||
<li>Historic Merchant’s Xmas, Hamilton ON (2009)</li>
|
||||
<li>Historic Merchant's Xmas, Hamilton ON (2009)</li>
|
||||
<li>History Symposium, London ON (2019)</li>
|
||||
<li>Artifact Day, Longwoods Road Conservation Area (2022)</li>
|
||||
</ul>
|
||||
@@ -61,7 +65,8 @@
|
||||
<div class="mx-3">
|
||||
<h4 class="mb-2">Re-enactments</h4>
|
||||
<ul class="mt-0">
|
||||
<li>Fort Malden, Amherstburg ON (2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2015, 2016)</li>
|
||||
<li>Hastings Medieval Festival, Hastings, ON (2025)</li>
|
||||
<li>Fort Malden, Amherstburg ON (2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2015, 2016, 2024, 2025, 2026)</li>
|
||||
<li>Roman Market Days (2004)</li>
|
||||
<li>NE Route March, Erie Canal (2005)</li>
|
||||
<li>Roman Days (2005)</li>
|
||||
@@ -87,14 +92,14 @@
|
||||
</ul>
|
||||
<p class="mb-2">It continues to host our annual events:</p>
|
||||
<ul class="mt-0">
|
||||
<li>Castra Aestiva (2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2022, 2023)</li>
|
||||
<li>Castra Hiberna (2017, 2018, 2021, 2022, 2023)</li>
|
||||
<li>Castra Aestiva (2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2022, 2023, 2024, 2025, 2026)</li>
|
||||
<li>Castra Hiberna (2017, 2018, 2021, 2022, 2023, 2024, 2025)</li>
|
||||
</ul>
|
||||
<h2>Special Thanks</h2>
|
||||
<p>Tom Ross - Legio XXX's <em>patronus</em> (patron) providing re-enactment facilities</p>
|
||||
<p>Robert Sacco - <em>Aquilifer</em> (Eagle bearer) & Legio XXX's president</p>
|
||||
<p>David Blain - <em>Tribunus angusticlavius</em> (staff officer) & subject matter expert</p>
|
||||
<p>Robert Norton - <em>Armicustos</em> (Quartermaster) who provides equipment, repairs & <a routerLink="/diy">manuals</a></p>
|
||||
<p>Robert Norton - <em>Armicustos</em> (Quartermaster) who provides equipment, repairs & <a routerLink="/equipment">manuals</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
</p>
|
||||
<h2 class="mb-2">Details</h2>
|
||||
<p>
|
||||
Tom Ross hosts a Castra Aestiva event every Memorial Day weekend (in May) near Tillsonburg Ontario. It's a private multi-day training event involving Romans and their enemies and is the perfect opportunity for new Roman reenactors. Participants
|
||||
Tom Ross hosts a Castra Aestiva event every May near Tillsonburg Ontario. It's a private multi-day training event involving Romans and their enemies and is the perfect opportunity for new Roman reenactors. Participants
|
||||
sleep in <em>sub pellibus</em> (tents) within the protective walls of the <em>castrum</em> (fort). If May doesn't work for you, consider coming to <a routerLink="/events/castra-hiberna">Castra Hiberna</a> in September.
|
||||
</p>
|
||||
<h3 class="mb-0">Date: Memorial Day Weekend, Annually</h3>
|
||||
<h3 class="mb-0">Date: May, Annually</h3>
|
||||
<h3 class="mb-0">Location: Tillsonburg, Ontario, Canada</h3>
|
||||
<h3 class="mb-0">Activities:</h3>
|
||||
<ul class="mt-0">
|
||||
|
||||
@@ -2,12 +2,105 @@
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div class="d-flex align-items-end justify-content-between mb-2">
|
||||
<h1 class="mb-0">Calendar</h1>
|
||||
<a class="d-none d-print-none d-sm-flex justify-content-center text-muted" href="javascript:window.print()">
|
||||
<mat-icon class="me-1">print</mat-icon>
|
||||
Print
|
||||
</a>
|
||||
<div class="d-flex gap-3 align-items-center">
|
||||
<ng-container *ngIf="momentum.admin | async">
|
||||
<button class="d-print-none" mat-raised-button color="primary" (click)="openCreate()">
|
||||
<mat-icon>add</mat-icon> Add Event
|
||||
</button>
|
||||
</ng-container>
|
||||
<a class="d-none d-print-none d-sm-flex justify-content-center text-muted" href="javascript:window.print()">
|
||||
<mat-icon class="me-1">print</mat-icon>
|
||||
Print
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="mb-4"></mat-divider>
|
||||
<iframe src="https://calendar.google.com/calendar/embed?height=600&wkst=1&bgcolor=%23ffffff&ctz=America%2FToronto&showTitle=0&showNav=1&showPrint=1&showTabs=0&showCalendars=0&src=Y18wYTIxNTM3ZDgwMWMzNTQ0MTUwYzk5YTUwNjI5Yjc5MjM0MjYyYjY0YzYyNzZiZTQ3OWJlMzA4OWU0MmM3NTQ5QGdyb3VwLmNhbGVuZGFyLmdvb2dsZS5jb20&color=%23D50000" style="border-width:0" width="100%" height="600" frameborder="0" scrolling="no"></iframe>
|
||||
|
||||
<div class="d-flex align-items-stretch mb-5 p-3" style="background:#f6eed9">
|
||||
<div style="width:3px;background:currentColor;opacity:.4" class="me-3 flex-shrink-0"></div>
|
||||
<div class="py-1">
|
||||
<div class="text-muted small text-uppercase" style="letter-spacing:.15em">Every Month</div>
|
||||
<div class="fw-bold fs-5">Monthly Online Meetup</div>
|
||||
<div class="text-muted small mb-2">Join us online for our regular group gathering</div>
|
||||
<a class="d-print-none" mat-stroked-button href="/register">Register Now</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!loading; else spinner">
|
||||
<ng-container *ngIf="events.length">
|
||||
<div *ngFor="let group of groupedEvents" class="mb-5">
|
||||
<div class="text-uppercase small fw-bold mb-3" style="letter-spacing:.2em">{{group.month}}</div>
|
||||
|
||||
<!-- Added print-border class and edit button -->
|
||||
<div *ngFor="let event of group.events" class="d-flex align-items-stretch mb-3 p-3 event-card" style="background:#f6eed9">
|
||||
<div class="text-muted small text-end flex-shrink-0 pe-3" style="min-width:3.5rem">
|
||||
<div class="fw-bold" style="font-size:1.1rem;line-height:1">{{event.date | date:'d'}}</div>
|
||||
<div>{{event.date | date:'EEE'}}</div>
|
||||
<div *ngIf="event.endDate" class="mt-1" style="border-top:1px solid currentColor;opacity:.4;padding-top:.25rem">
|
||||
<div class="fw-bold" style="font-size:1.1rem;line-height:1">{{event.endDate | date:'d'}}</div>
|
||||
<div>{{event.endDate | date:'EEE'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width:2px;background:currentColor;opacity:.15" class="flex-shrink-0 me-3"></div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-bold">{{event.title}}</div>
|
||||
<div class="text-muted small" *ngIf="event.location">{{event.location}}</div>
|
||||
<div class="text-muted small" *ngIf="!event.endDate">{{event.date | date:'h:mm a'}}</div>
|
||||
<div class="text-muted small" *ngIf="event.endDate">
|
||||
{{event.date | date:'MMM d, h:mm a'}} – {{event.endDate | date:'MMM d, h:mm a'}}
|
||||
</div>
|
||||
<div class="text-muted small fst-italic mt-1" *ngIf="event.description">{{event.description}}</div>
|
||||
</div>
|
||||
<!-- Edit button, screen only -->
|
||||
<ng-container *ngIf="momentum.admin | async">
|
||||
<button class="d-print-none align-self-start ms-2" mat-icon-button (click)="openEdit(event)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button class="d-print-none align-self-start" mat-icon-button (click)="deleteEvent(event)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #spinner>
|
||||
<div class="text-center py-5"><mat-spinner class="mx-auto"></mat-spinner></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create/Edit Dialog -->
|
||||
<ng-template #createDialog>
|
||||
<h2 mat-dialog-title>{{editingId ? 'Edit Event' : 'Add Event'}}</h2>
|
||||
<mat-dialog-content>
|
||||
<mat-form-field class="w-100 mb-2">
|
||||
<mat-label>Title</mat-label>
|
||||
<input matInput [(ngModel)]="form.title" required />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="w-100 mb-2">
|
||||
<mat-label>Date & Time</mat-label>
|
||||
<input matInput type="datetime-local" [(ngModel)]="form.date" required />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="w-100 mb-2">
|
||||
<mat-label>End Date & Time (optional)</mat-label>
|
||||
<input matInput type="datetime-local" [(ngModel)]="form.endDate" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="w-100 mb-2">
|
||||
<mat-label>Location</mat-label>
|
||||
<input matInput [(ngModel)]="form.location" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea matInput [(ngModel)]="form.description" rows="3"></textarea>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button mat-raised-button color="primary" (click)="saveEvent()" [disabled]="saving">
|
||||
{{saving ? 'Saving...' : 'Save'}}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,7 +1,90 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MomentumService} from '../../../services/momentum.service';
|
||||
|
||||
type CalEvent = {
|
||||
_id?: string;
|
||||
title: string;
|
||||
date: string;
|
||||
endDate?: string;
|
||||
location?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type EventGroup = {
|
||||
month: string;
|
||||
events: CalEvent[];
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-calendar',
|
||||
templateUrl: './calendar.component.html'
|
||||
})
|
||||
export class CalendarComponent { }
|
||||
export class CalendarComponent implements OnInit {
|
||||
@ViewChild('createDialog') createDialog!: TemplateRef<any>;
|
||||
|
||||
events: CalEvent[] = [];
|
||||
groupedEvents: EventGroup[] = [];
|
||||
loading = true;
|
||||
saving = false;
|
||||
form: Partial<CalEvent> = {};
|
||||
editingId: string | null = null;
|
||||
|
||||
constructor(private dialog: MatDialog, public momentum: MomentumService) {}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.loadEvents();
|
||||
}
|
||||
|
||||
async deleteEvent(event: CalEvent) {
|
||||
if (!event._id || !confirm(`Delete "${event.title}"?`)) return;
|
||||
await this.momentum.api.data.delete('Events', <any>event._id);
|
||||
await this.loadEvents();
|
||||
}
|
||||
|
||||
async loadEvents() {
|
||||
this.loading = true;
|
||||
const raw = await this.momentum.api.data.read<CalEvent>('Events') as CalEvent[];
|
||||
const now = new Date();
|
||||
this.events = raw
|
||||
.filter(e => new Date(e.date) >= now)
|
||||
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
||||
this.groupedEvents = this.groupByMonth(this.events);
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
groupByMonth(events: CalEvent[]): EventGroup[] {
|
||||
const map = new Map<string, CalEvent[]>();
|
||||
for (const e of events) {
|
||||
const key = new Date(e.date).toLocaleDateString('en-US', {month: 'long', year: 'numeric'});
|
||||
if (!map.has(key)) map.set(key, []);
|
||||
map.get(key)!.push(e);
|
||||
}
|
||||
return Array.from(map.entries()).map(([month, events]) => ({month, events}));
|
||||
}
|
||||
|
||||
openCreate() {
|
||||
this.editingId = null;
|
||||
this.form = {};
|
||||
this.dialog.open(this.createDialog, {width: '480px'});
|
||||
}
|
||||
|
||||
openEdit(event: CalEvent) {
|
||||
this.editingId = event._id ?? null;
|
||||
this.form = {...event};
|
||||
this.dialog.open(this.createDialog, {width: '480px'});
|
||||
}
|
||||
|
||||
async saveEvent() {
|
||||
if (!this.form.title || !this.form.date) return;
|
||||
this.saving = true;
|
||||
if (this.editingId) {
|
||||
await this.momentum.api.data.update<CalEvent>('Events', <any>{_id: this.editingId, ...this.form});
|
||||
} else {
|
||||
await this.momentum.api.data.create<CalEvent>('Events', <any>this.form);
|
||||
}
|
||||
this.dialog.closeAll();
|
||||
this.saving = false;
|
||||
await this.loadEvents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<p>In the fall, the indigenous people had to focus their efforts on harvesting crops & preparing for the cold harsh winter. This left little time for raids & hostilities giving Roman soldiers time to rest, train & recoup.</p>
|
||||
<h2 class="mb-2">Details</h2>
|
||||
<p>
|
||||
Tom Ross hosts a Castra Hiberna event every September near Tillsonburg Ontario. It's a private single day event between Romans and their adversaries. It's an excellent opportunity for new Roman reenactors. If May doesn't work for you, consider coming to
|
||||
Tom Ross hosts a Castra Hiberna event every September near Tillsonburg Ontario. It's a private single day event between Romans and their adversaries. It's an excellent opportunity for new Roman reenactors. If September doesn't work for you, consider coming to
|
||||
<a routerLink="/events/castra-aestiva">Castra Aestiva</a> in May.
|
||||
</p>
|
||||
<h3 class="mb-0">Date: September, Annually</h3>
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
<div class="invert p-5">
|
||||
<div *ngFor="let album of photos" class="mb-5">
|
||||
<h1 class="mb-0">{{album.album}}</h1>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="display-4 mb-0">Gallery</h1>
|
||||
|
||||
<ng-container *ngIf="momentum.isLoggedIn | async">
|
||||
<button mat-raised-button color="primary" [matMenuTriggerFor]="uploadMenu">
|
||||
<mat-icon>upload</mat-icon> Upload Photos
|
||||
</button>
|
||||
|
||||
<mat-menu #uploadMenu="matMenu">
|
||||
<button mat-menu-item *ngFor="let y of uploadYears" (click)="upload(y)">{{y}}</button>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let album of photos" class="mb-5" (inView)="loadAlbum(album)">
|
||||
<h1 class="display-6 mb-0">{{album.album}}</h1>
|
||||
<mat-divider class="mb-3"></mat-divider>
|
||||
<div *ngFor="let photo of album.photos; let i = index" class="d-inline-block me-3 mb-3">
|
||||
<div *ngFor="let photo of album.photos" class="d-inline-block me-3 mb-3">
|
||||
<xxx-placeholder [src]="photo.src" [alt]="photo.alt" height="150px" (click)="open(photo)"></xxx-placeholder>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,3 +2,17 @@ xxx-placeholder:hover ::ng-deep img {
|
||||
transform: scale(1.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.photo-wrap {
|
||||
.delete-btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
transform: scale(0.75);
|
||||
}
|
||||
|
||||
&:hover .delete-btn {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,196 +1,74 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MomentumService} from '../../services/momentum.service';
|
||||
import {ImageViewerComponent} from '../../components/image-viewer/image-viewer.component';
|
||||
import {Photo} from '../../components/models/photo';
|
||||
|
||||
type Album = {album: string, photos: Photo[], loaded: boolean};
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-gallery',
|
||||
templateUrl: './gallery.component.html',
|
||||
styleUrls: ['./gallery.component.scss'],
|
||||
})
|
||||
export class GalleryComponent {
|
||||
photos: {album: string, photos: Photo[]}[] = [
|
||||
{album: '2023', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2023/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/010.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2023/011.jpg'},
|
||||
]}, {album: '2022', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2022/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/010.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/011.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/012.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/013.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/014.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/015.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/016.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2022/017.jpg'},
|
||||
]}, {album: '2021', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2021/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2021/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2021/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2021/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2021/005.jpg'},
|
||||
]}, {album: '2019', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2019/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2019/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2019/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2019/004.jpg'},
|
||||
]}, {album: '2018', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2018/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2018/008.jpg'},
|
||||
]}, {album: '2017', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2017/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2017/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2017/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2017/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2017/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2017/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2017/007.jpg'},
|
||||
]}, {album: '2016', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2016/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/010.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/011.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/012.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/013.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/014.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/015.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/016.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/017.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/018.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2016/019.jpg'},
|
||||
]}, {album: '2014', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2014/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2014/010.jpg'},
|
||||
]}, {album: '2013', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2013/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2013/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2013/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2013/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2013/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2013/006.jpg'},
|
||||
]}, {album: '2012', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2012/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/010.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/011.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/012.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/013.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/014.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/015.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2012/016.jpg'},
|
||||
]}, {album: '2011', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2011/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2011/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2011/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2011/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2011/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2011/006.jpg'},
|
||||
]}, {album: '2010', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2010/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/010.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/011.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/012.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2010/013.jpg'},
|
||||
]}, {album: '2009', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2009/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/008.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/009.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/010.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/011.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2009/012.jpg'},
|
||||
]}, {album: '2008', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2008/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/006.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/007.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2008/008.jpg'},
|
||||
]}, {album: '2007', photos: [
|
||||
{alt: '', src: '/assets/img/gallery/2007/001.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2007/002.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2007/003.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2007/004.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2007/005.jpg'},
|
||||
{alt: '', src: '/assets/img/gallery/2007/006.jpg'},
|
||||
]}];
|
||||
export class GalleryComponent implements OnInit {
|
||||
photos: Album[] = [];
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
constructor(private dialog: MatDialog, public momentum: MomentumService) {}
|
||||
|
||||
get flatten() {
|
||||
return this.photos.reduce((acc: any[], album) => {
|
||||
return [...acc, ...album.photos];
|
||||
}, []);
|
||||
uploadYears = Array.from({length: new Date().getFullYear() - 2006}, (_, i) => 2007 + i).reverse();
|
||||
|
||||
async upload(year: number) {
|
||||
await this.momentum.api.storage.upload(`Photos/Gallery/${year}`, undefined, {multiple: true, accept: 'image/*'});
|
||||
await this.loadAlbums();
|
||||
}
|
||||
|
||||
open(photo: any) {
|
||||
async ngOnInit() {
|
||||
await this.loadAlbums();
|
||||
}
|
||||
|
||||
async loadAlbums() {
|
||||
const files = await this.momentum.api.storage.all('Photos/Gallery');
|
||||
const albumMap = new Map<string, null>();
|
||||
|
||||
for (const file of files.filter((f: any) => f.mime !== 'directory')) {
|
||||
const album = file.path.split('/')[3];
|
||||
albumMap.set(album, null);
|
||||
}
|
||||
|
||||
this.photos = [...albumMap.keys()]
|
||||
.sort((a, b) => +b - +a)
|
||||
.map(album => ({album, photos: [], loaded: false}));
|
||||
}
|
||||
|
||||
async loadAlbum(album: Album) {
|
||||
if (album.loaded) return;
|
||||
album.loaded = true;
|
||||
|
||||
const files = await this.momentum.api.storage.all(`Photos/Gallery/${album.album}`);
|
||||
album.photos = files
|
||||
.filter((f: any) => f.mime !== 'directory')
|
||||
.map((file: any) => ({
|
||||
alt: file.name,
|
||||
src: this.momentum.api.storage.open(file.path, false) as string,
|
||||
path: file.path
|
||||
}));
|
||||
}
|
||||
|
||||
get flatten() {
|
||||
return this.photos.reduce((acc: Photo[], album) => [...acc, ...album.photos], []);
|
||||
}
|
||||
|
||||
open(photo: Photo) {
|
||||
const flat = this.flatten;
|
||||
const index = flat.findIndex(p => p.src == photo.src);
|
||||
const index = flat.findIndex(p => p.src === photo.src);
|
||||
this.dialog.open(ImageViewerComponent, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
autoFocus: false,
|
||||
data: {index, photos: flat}
|
||||
}).afterClosed().subscribe(result => {
|
||||
if (result?.deleted) this.loadAlbums();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<header id="banner" style="height: calc(100vh - 64px)">
|
||||
<xxx-banner></xxx-banner>
|
||||
</header>
|
||||
|
||||
<!-- ABout -->
|
||||
<section id="about" class="d-flex flex-column flex-md-row align-items-center justify-content-center" style="height: 100vh">
|
||||
<div class="d-none d-md-inline flex-grow-1 text-end" style="flex-basis: 0">
|
||||
@@ -20,34 +21,38 @@
|
||||
that recreate the lives of soldiers found in Trajan's legions during the 1st - 2nd Century AD
|
||||
</p>
|
||||
</div>
|
||||
<a routerLink="/about">More</a>
|
||||
<a routerLink="/about" class="d-block mt-4" style="font-size: 2em;">More >></a>
|
||||
</div>
|
||||
<div class="flex-md-grow-1" style="flex-basis: 0">
|
||||
<img class="mt-5" src="/assets/img/standard.png" alt="Legio XXX Standard" height="250px" width="auto">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Discord -->
|
||||
<section class="d-flex" style="background-color: #990000">
|
||||
<div class="d-flex flex-grow-1">
|
||||
<div class="h-100 w-100" style="background: #7289d9"></div>
|
||||
<img class="d-block d-md-none" src="/assets/img/discord.png" style="height: 100px; transform: translateX(-1px);">
|
||||
<img class="d-none d-md-block" src="/assets/img/discord.png" style="height: 200px; transform: translateX(-1px);">
|
||||
<img class="d-block d-md-none" src="/assets/img/calendar-banner.png" style="height: 100px; transform: translateX(-1px);">
|
||||
<img class="d-none d-md-block" src="/assets/img/calendar-banner.png" style="height: 200px; transform: translateX(-1px);">
|
||||
</div>
|
||||
<div class="d-flex justify-content-start align-items-center flex-grow-1 p-3">
|
||||
<div class="d-block text-center">
|
||||
<h1 class="d-block m-0 mb-md-3 transparent-link">
|
||||
<a href="https://discord.gg/wW458KYR79" target="_blank">Join us on Discord</a>
|
||||
<a href="https://discord.gg/wW458KYR79" target="_blank">⚔️ Witness the Glory</a>
|
||||
</h1>
|
||||
<div class="d-none d-md-inline">
|
||||
<p>
|
||||
Ask us questions, get involved
|
||||
<br><br>
|
||||
and celebrate the glory of Rome with us!
|
||||
Join our monthly meetings
|
||||
<br>
|
||||
Experience the history and spirit of the legion
|
||||
<br>
|
||||
<a [routerLink]="['/register']" class="text-white fw-bold">JOIN NOW</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Links -->
|
||||
<section id="resources" class="d-flex flex-column flex-md-row align-items-center justify-content-center invert" style="min-height: 50vh; max-height: 100vh; background: url('/assets/img/texture.png') repeat;">
|
||||
<div class="py-5 container">
|
||||
@@ -59,7 +64,7 @@
|
||||
<div>
|
||||
<h3 class="mb-1">Legio XXX</h3>
|
||||
<p class="m-0">Learn about Legio XXX, who we are & what we do</p>
|
||||
<a routerLink="/about">About Us</a> / <a routerLink="/gallery">Gallery</a> / <a routerLink="/" fragment="contact">Contact</a>
|
||||
<a routerLink="/about">About Us</a> / <a routerLink="/" fragment="contact">Contact</a> / <a routerLink="/gallery">Gallery</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex p-3 pe-4 rounded-1 bg-light m-3 align-items-center border" style="min-width: 45%; flex: 1 0 0;">
|
||||
@@ -89,19 +94,20 @@
|
||||
<div>
|
||||
<h3 class="mb-1">Equipment</h3>
|
||||
<p class="m-0">Learn how to assemble & maintain your kit</p>
|
||||
<a routerLink="/buy">Buy Equipment</a> / <a routerLink="/diy">Build Equipment</a> / <a routerLink="/maintenance">Maintenance</a>
|
||||
<a routerLink="/equipment">Acquire Equipment</a> / <a routerLink="/maintenance">Maintenance</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Recruitment -->
|
||||
<section id="recruitment" class="d-flex flex-column flex-md-row align-items-center justify-content-center invert" style="padding: 10rem 0">
|
||||
<div class="flex-grow-1 mt-5 mt-md-0 p-5 pb-0 text-center text-md-end" style="flex-basis: 0">
|
||||
<img src="/assets/img/recruitment.png" alt="Recruitment poster" style="max-width: min(90%, 400px)">
|
||||
</div>
|
||||
<div class="d-flex flex-grow-1 justify-content-start align-items-center" style="flex-basis: 0">
|
||||
<div class="d-block text-center">
|
||||
<div class="d-block text-center d-flex flex-column align-items-center">
|
||||
<h2 class="my-3">Enlist Today!</h2>
|
||||
<p>
|
||||
Interested in any <a routerLink="/events/calendar">events</a>?
|
||||
@@ -112,13 +118,18 @@
|
||||
<br><br>
|
||||
Family participation is welcome!
|
||||
</p>
|
||||
<div class="d-flex align-items-center">
|
||||
<a [routerLink]="['/register']" class="btn btn-light btn-lg" style="background: var(--theme-primary); color: var(--theme-primary-contrast)">JOIN NOW</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Page Break-->
|
||||
<section style="background: #fff">
|
||||
<div style="height: 300px; background: 0 url('/assets/img/formation.png') repeat-x; background-size: auto 300px;"></div>
|
||||
</section>
|
||||
|
||||
<!-- Contact -->
|
||||
<section id="contact" class="d-flex flex-column align-items-center justify-content-center" style="padding: 10rem 0">
|
||||
<div class="container">
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<div class="invert">
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex align-items-end justify-content-between mb-2">
|
||||
<h1 class="mb-0">Buy Equipment</h1>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><a href="https://armae.com/en" target="_blank">Armæ</a></li>
|
||||
<li><a href="https://www.facebook.com/FabricaCacti" target="_blank">Fabrica Cacti</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-buy',
|
||||
templateUrl: './buy.component.html'
|
||||
})
|
||||
export class BuyComponent { }
|
||||
@@ -1,21 +0,0 @@
|
||||
<div class="invert">
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex align-items-end justify-content-between mb-2">
|
||||
<h1 class="mb-0">Build Equipment</h1>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><em>Beltus</em> (Belt): <a href="/assets/manuals/Beltus%20Part%201%20-%20Robert%20Norton.pdf" target="_blank"> Part 1</a>, <a href="/assets/manuals/Beltus%20Part%202%20-%20Robert%20Norton.pdf" target="_blank">Part 2</a></li>
|
||||
<li><a href="/assets/manuals/Calcei%20-%20Lee%20Holeva.pdf" target="_blank"><em>Calcei</em> (Boots)</a></li>
|
||||
<li><a href="/assets/manuals/Caligae%20-%20Robert%20Norton.pdf" target="_blank"><em>Caligae</em> (Sandals)</a></li>
|
||||
<li><a href="/assets/manuals/Helmet%20Liner%20-%20Robert%20Norton.pdf" target="_blank">Helmet Liner</a></li>
|
||||
<li><a href="/assets/manuals/Loculus%20-%20Robert%20Norton.pdf" target="_blank"><em>Loculus</em> (Bag)</a></li>
|
||||
<li><em>Lorica Hamata</em> (Chain-mail): <a href="/assets/manuals/Lorica%20Hamata%20Part%201.pdf" target="_blank"> Part 1</a>, <a href="/assets/manuals/Lorica%20Hamata%20Part%202.pdf" target="_blank">Part 2</a></li>
|
||||
<li><a href="/assets/manuals/Scutum.pdf" target="_blank"><em>Scutum</em> (Shield)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
<tr>
|
||||
<td><em>Laxate</em></td>
|
||||
<td>At Ease</td>
|
||||
<td>Relax with feet shoulder's width apart, equipment may rest on the ground; maintain discipline (don't move feet, face the front & remain quite).</td>
|
||||
<td>Relax with feet shoulder's width apart, equipment may rest on the ground; maintain discipline (don't move feet, face the front & remain quiet).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Vos Dimitto</em></td>
|
||||
@@ -153,12 +153,12 @@
|
||||
<tr>
|
||||
<td><em>Aciem formate</em></td>
|
||||
<td>Battle line</td>
|
||||
<td>Soldiers form a line with their <em>pilum</em> at the ready. This is not a shield wall, 6' spacing should be maintained between each column.</td>
|
||||
<td>Soldiers form a line with their <em>pilum</em> at the ready. Maintain 6' spacing between each column.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Cuneum formate</em></td>
|
||||
<td>Wedge</td>
|
||||
<td>Centerio is placed at the tip of the wedge with soldiers trailing off his side.</td>
|
||||
<td>The centurio is placed at the tip of the wedge with soldiers trailing off his side.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Orbem formate</em></td>
|
||||
@@ -172,13 +172,13 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Testudinem formate</em></td>
|
||||
<td><em>Testudo</em> (Tortis)</td>
|
||||
<td><em>Testudo</em> (Tortoise)</td>
|
||||
<td>First rank raises shields to eye height, the following ranks lift their shields onto their heads overlapping the rank in-front. Formation advances together using a chant to stay in step ("Ro-ma, Ro-ma" or "Dex-Sin, Dex-Sin").</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Porro</em></td>
|
||||
<td>Advance</td>
|
||||
<td>Move forward keeping formation with weapons drawn.</td>
|
||||
<td>Move forward (charge) keeping formation with weapons drawn.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Ordinem servate</em></td>
|
||||
@@ -230,7 +230,7 @@
|
||||
<th colspan="3">Weapons: Artillery</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Funditor</em></td>
|
||||
<td><em>Fundatores</em></td>
|
||||
<td>Slingers</td>
|
||||
<td>Following command is for slingers only.</td>
|
||||
</tr>
|
||||
@@ -242,7 +242,7 @@
|
||||
<tr>
|
||||
<td><em>Ballistam</em></td>
|
||||
<td>Bolt thrower</td>
|
||||
<td>Following command is for <em>scropios</em> / <em>balistas</em>.</td>
|
||||
<td>Following command is for <em>scropio</em> / <em>balista</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Catapultam</em></td>
|
||||
@@ -304,13 +304,13 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Sin[ister]/Gladios</em></td>
|
||||
<td>Right/Sword [hand]</td>
|
||||
<td>Legio XXX prefers to use <em>gladios</em> instead of <em>sin</em>.</td>
|
||||
<td>Left/Shield [hand]</td>
|
||||
<td>Legio XXX prefers to use <em>scuta</em> instead of <em>dex</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Dex[ter]/Scuta</em></td>
|
||||
<td>Left/Shield [hand]</td>
|
||||
<td>Legio XXX prefers to use <em>scuta</em> instead of <em>dex</em>.</td>
|
||||
<td>Right/Sword [hand]</td>
|
||||
<td>Legio XXX prefers to use <em>gladios</em> instead of <em>sin</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><em>Clamate</em></td>
|
||||
|
||||
40
src/app/views/reenact/equipment/equipment.component.html
Normal file
40
src/app/views/reenact/equipment/equipment.component.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<div class="invert">
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div class="row">
|
||||
<!-- Buy Equipment Card -->
|
||||
<div class="col-12 col-md-6 mb-4">
|
||||
<div class="p-4 border rounded shadow-sm">
|
||||
<div class="mb-3">
|
||||
<h1 class="mb-0">Buy Equipment</h1>
|
||||
<mat-divider class="mt-2"></mat-divider>
|
||||
</div>
|
||||
<ul class="mt-3">
|
||||
<li><a href="https://armae.com/en" target="_blank">Arma</a></li>
|
||||
<li><a href="https://www.by-the-sword.com/">By The Sword</a></li>
|
||||
<li><a href="https://www.facebook.com/FabricaCacti" target="_blank">Fabrica Cacti</a></li>
|
||||
<li><a href="http://www.lawrensnest.com">La Wren's Nests</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Build Equipment Card -->
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="p-4 border rounded shadow-sm">
|
||||
<div class="mb-3">
|
||||
<h1 class="mb-0">Build Equipment</h1>
|
||||
<mat-divider class="mt-2"></mat-divider>
|
||||
</div>
|
||||
<ul class="mt-3">
|
||||
<li><em>Balteus</em> (Belt): <a href="/assets/manuals/Beltus%20Part%201%20-%20Robert%20Norton.pdf" target="_blank"> Part 1</a>, <a href="/assets/manuals/Beltus%20Part%202%20-%20Robert%20Norton.pdf" target="_blank">Part 2</a></li>
|
||||
<li><a href="/assets/manuals/Calcei%20-%20Lee%20Holeva.pdf" target="_blank"><em>Calcei</em> (Boots)</a></li>
|
||||
<li><a href="/assets/manuals/Caligae%20-%20Robert%20Norton.pdf" target="_blank"><em>Caligae</em> (Sandals)</a></li>
|
||||
<li><a href="/assets/manuals/Helmet%20Liner%20-%20Robert%20Norton.pdf" target="_blank">Helmet Liner</a></li>
|
||||
<li><a href="/assets/manuals/Loculus%20-%20Robert%20Norton.pdf" target="_blank"><em>Loculus</em> (Bag)</a></li>
|
||||
<li><em>Lorica Hamata</em> (Chain-mail): <a href="/assets/manuals/Lorica%20Hamata%20Part%201.pdf" target="_blank"> Part 1</a>, <a href="/assets/manuals/Lorica%20Hamata%20Part%202.pdf" target="_blank">Part 2</a></li>
|
||||
<li><a href="/assets/manuals/Scutum.pdf" target="_blank"><em>Scutum</em> (Shield)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,6 +2,6 @@ import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-diy',
|
||||
templateUrl: './diy.component.html'
|
||||
templateUrl: './equipment.component.html'
|
||||
})
|
||||
export class DiyComponent { }
|
||||
export class EquipmentComponent { }
|
||||
@@ -24,7 +24,7 @@
|
||||
<h2 class="mb-2">Enlisting</h2>
|
||||
<p>
|
||||
If reenacting is something you would seriously like to do, great! Interest in the history of Rome is the primary requirement and you are in the right place. This guide will walk you through everything
|
||||
you need to know to get started. Acquiring a full kit may take some time & cost a couple thousand dollars (USD/CAD) however we provide <a routerLink="/diy">manuals</a> to build equipment yourself
|
||||
you need to know to get started. Acquiring a full kit may take some time & cost a couple thousand dollars (USD/CAD) however we provide <a routerLink="/equipment">manuals</a> to build equipment yourself
|
||||
to save on costs. With enough notice we may be able to loan any equipment you are missing for an event, please let us know.
|
||||
</p>
|
||||
<p>
|
||||
@@ -35,6 +35,11 @@
|
||||
<div class="d-flex flex-column flex-md-row justify-content-md-between">
|
||||
<div class="me-md-3">
|
||||
<ul class="mt-0">
|
||||
<li>Enlist with your local reenactment group:
|
||||
<ul>
|
||||
<li><strong>Southern-Ontario</strong> - <a routerLink="/register">Enlist with Legio XXX</a></li>
|
||||
<li><em>Elsewhere</em> - <a href="https://www.romanempire.net/roman_reenactment_groups.html" target="_blank">Find your local recruiter</a></li>
|
||||
</ul>
|
||||
<li>Familiarize yourself with our <a routerLink="/rules">Rules & Regulations</a></li>
|
||||
<li>Pick out a Roman name:</li>
|
||||
<ul>
|
||||
@@ -42,7 +47,7 @@
|
||||
<li>Can be fictional or non-fictional</li>
|
||||
<li>Avoid famous people/names</li>
|
||||
</ul>
|
||||
<li><a routerLink="/vendors">Purchase</a>, <a routerLink="/diy">build</a> or arrange for loaner equipment by reaching out. Bold items are considered the minimum camp attire:</li>
|
||||
<li><a routerLink="/equipment">Purchase, build</a> or arrange for equipment by reaching out. Bold items are minimum camp attire:</li>
|
||||
<ul>
|
||||
<li><span class="fw-bold"><em>Tunica</em> (Tunic)</span> - A large shirt/dress held at the waist by a cord or cloth belt</li>
|
||||
<li><span class="fw-bold"><em>Caligae</em> (Sandals)</span> - Modern leather sandals will do in a pinch</li>
|
||||
@@ -56,7 +61,7 @@
|
||||
<li><em>Subarmalis</em> (Padded Vest) - Worn under your armour for comfort and protection</li>
|
||||
<li><em>Lorica</em> (Armour) - Segmata, hamata or plumata/squamta</li>
|
||||
</ul>
|
||||
<li>Submit an <a href="/assets/docs/Application.pdf" target="_blank">application</a> to Robert Sacco: <a href="mailto:primuspiluslxxx@gmail.com" target="_blank">primuspiluslxxx@gmail.com</a></li>
|
||||
<li><a routerLink="/events/calendar">Go to your first event!</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex d-print-none align-items-center justify-content-center pt-4">
|
||||
|
||||
129
src/app/views/register/register.component.html
Normal file
129
src/app/views/register/register.component.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<div class="invert">
|
||||
<div class="banner d-print-none"></div>
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div>
|
||||
<img src="/assets/img/header.png" alt="Legio XXX" class="mx-auto d-block" style="width: 100%; height: auto;">
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div *ngIf="success" class="alert alert-success">
|
||||
✅ Your registration has been submitted! We will be in touch soon.
|
||||
</div>
|
||||
<div *ngIf="error" class="alert alert-danger">
|
||||
⚠️ Something went wrong, please try again or contact us directly.
|
||||
</div>
|
||||
|
||||
<form #form="ngForm" (ngSubmit)="submit(form.value)" [class.locked]="success">
|
||||
<h3 class="fw-bold mb-2">Member Information</h3>
|
||||
<h4 class="bg-black p-2 text-center text-white fw-bold">Personal Information</h4>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="field col-4">
|
||||
<label>Last Name <span class="required">*</span></label>
|
||||
<input ngModel name="lastName" required [disabled]="success">
|
||||
</div>
|
||||
<div class="field col-4">
|
||||
<label>First Name <span class="required">*</span></label>
|
||||
<input ngModel name="firstName" required [disabled]="success">
|
||||
</div>
|
||||
<div class="field col-4">
|
||||
<label>Date of Birth <span class="required">*</span></label>
|
||||
<input ngModel name="dateOfBirth" type="date" required [disabled]="success">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="field col-8">
|
||||
<label>Street Address</label>
|
||||
<input ngModel name="streetAddress" [disabled]="success">
|
||||
</div>
|
||||
<div class="field col-4">
|
||||
<label>Apartment/Unit #</label>
|
||||
<input ngModel name="unit" [disabled]="success">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="field col-4">
|
||||
<label>City</label>
|
||||
<input ngModel name="city" [disabled]="success">
|
||||
</div>
|
||||
<div class="field col-4">
|
||||
<label>Province/State</label>
|
||||
<input ngModel name="province" [disabled]="success">
|
||||
</div>
|
||||
<div class="field col-4">
|
||||
<label>Postal Code</label>
|
||||
<input ngModel name="postalCode" [disabled]="success">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="field col-6">
|
||||
<label>Home Phone <span class="required">*</span></label>
|
||||
<input ngModel name="homePhone" type="tel" [disabled]="success" required>
|
||||
</div>
|
||||
<div class="field col-6">
|
||||
<label>Alternate Phone</label>
|
||||
<input ngModel name="alternatePhone" type="tel" [disabled]="success">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field mb-3">
|
||||
<label>Email <span class="required">*</span></label>
|
||||
<input ngModel name="email" type="email" required [disabled]="success">
|
||||
</div>
|
||||
|
||||
<h4 class="bg-black p-2 text-center text-white fw-bold">Reenacting</h4>
|
||||
|
||||
<div class="field mb-3">
|
||||
<label>Chosen Roman Name</label>
|
||||
<input ngModel name="romanName" [disabled]="success">
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="field col-6">
|
||||
<label>First Choice of Position</label>
|
||||
<input ngModel name="positionFirst" [disabled]="success">
|
||||
</div>
|
||||
<div class="field col-6">
|
||||
<label>Second Choice of Position</label>
|
||||
<input ngModel name="positionSecond" [disabled]="success">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field mb-3">
|
||||
<label>Have you ever re-enacted?</label>
|
||||
<input ngModel name="previousReenacting" [disabled]="success">
|
||||
</div>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
<div class="field mb-3">
|
||||
<label>Special Skills</label>
|
||||
<textarea ngModel name="specialSkills" rows="3" [disabled]="success"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="field mb-3">
|
||||
<label>Physical Limitations</label>
|
||||
<textarea ngModel name="physicalLimitations" rows="3" [disabled]="success"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="field mb-3">
|
||||
<label>Comments</label>
|
||||
<textarea ngModel name="comments" rows="5" [disabled]="success"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" [disabled]="form.invalid || loading || success">
|
||||
{{ loading ? 'Submitting...' : 'Submit Registration' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
100
src/app/views/register/register.component.ts
Normal file
100
src/app/views/register/register.component.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'xxx-register',
|
||||
templateUrl: './register.component.html',
|
||||
styles: [`
|
||||
.extra-spacing li { margin-bottom: 1em }
|
||||
|
||||
form { color: #000; }
|
||||
|
||||
hr { border-color: rgba(0,0,0,0.2); }
|
||||
|
||||
h3, h4 { color: #000; }
|
||||
|
||||
.alert {
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 1em;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.alert-success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.alert-danger {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.required { color: red; }
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.field label {
|
||||
font-size: 0.75em;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.field input,
|
||||
.field textarea {
|
||||
border: none;
|
||||
border-bottom: 2px solid #333;
|
||||
background: transparent;
|
||||
color: #000;
|
||||
font-size: 1em;
|
||||
padding: 4px 2px;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
font-family: inherit;
|
||||
}
|
||||
.field input:focus,
|
||||
.field textarea:focus {
|
||||
border-bottom-color: #000;
|
||||
}
|
||||
.field input:disabled,
|
||||
.field textarea:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.field textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
button[type=submit] {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 28px;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
button[type=submit]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class RegisterComponent {
|
||||
loading = false;
|
||||
success = false;
|
||||
error = false;
|
||||
|
||||
async submit(value: any) {
|
||||
this.loading = true;
|
||||
this.error = false;
|
||||
try {
|
||||
await window.momentum.data.create('Applications', value);
|
||||
this.success = true;
|
||||
} catch {
|
||||
this.error = true;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +1,167 @@
|
||||
<div class="invert">
|
||||
<div class="banner d-print-none"></div>
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div>
|
||||
<div class="d-flex align-items-end justify-content-between mb-2">
|
||||
<h1 class="mb-0">Resources</h1>
|
||||
<a class="d-none d-print-none d-sm-flex justify-content-center text-muted" href="javascript:window.print()">
|
||||
<mat-icon class="me-1">print</mat-icon>
|
||||
Print
|
||||
</a>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<br>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="mb-2">Websites</h2>
|
||||
<ul class="mt-0">
|
||||
<li><a href="https://www.legioix.org" target="_blank">Legio IX Hispana (https://legioix.org)</a></li>
|
||||
<li><a href="https://www.larp.com/legioxx/hndbk.html" target="_blank">Legio XX Handbook (https://larp.com/legioxx)</a></li>
|
||||
<li><a href="https://principialegionis.org" target="_blank">Principia Legionis (https://principialegionis.org)</a></li>
|
||||
<li><a href="http://romanempire.net/romepage/Links/roman_reenactment_groups.htm" target="_blank">Find a Reenactment Group (https://romanempire.net)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="mb-2">Modern</h2>
|
||||
<h3 class="mb-2">Life</h3>
|
||||
<ul>
|
||||
<li>Adkins, Lesley and Adkins, Roy A. <strong>Handbook to Life in Ancient Rome</strong>: New York: Oxford University Press, 1994</li>
|
||||
<li>Connolly, Peter and Dodge, Hazel. <strong>The Ancient City – Life in Classical Athens & Rome</strong>: Oxford: Oxford University Press, 2001</li>
|
||||
<li>Harlow, Mary and Laurence, Ray. <strong>Growing Up and Growing Old in Ancient Rome</strong>: London; Routlage, 2002</li>
|
||||
<li>Shelton, Jo-Ann. <strong>As the Romans Did</strong>: New York: Oxford University Press; 1998 (2nd ed.)</li>
|
||||
</ul>
|
||||
<h3 class="mb-2">Warfare</h3>
|
||||
<ul class="mt-0">
|
||||
<li>Bishop, M.C. & Coulston, J.C.N. <strong>Roman Military Equipment</strong>: Oxford: Oxbow Books, 2006</li>
|
||||
<li>Breeze, David J. <strong>The Frontiers of Imperial Rome</strong>: South Yorkshire: Pen & Sword Books Ltd., 2011</li>
|
||||
<li>Gilliver, Kate, Goldsworthy, Adrian, Whitby, Michael. <strong>Rome at War (Caesar and his Legacy)</strong>: Oxford: Osprey Publishing, 2005</li>
|
||||
<li>Goldsworthy, Adrian. <strong>The Complete Roman Army</strong>: London: Thames & Hudson Ltd., 2003</li>
|
||||
<li>Goldsworthy, Adrian. <strong>Roman Warfare</strong>: London: Cassell & Co., 2000</li>
|
||||
<li>Hyland, Ann. <strong>Training the Roman Cavalry (From Arrian’s Ars Tactica)</strong>: Gloucestershire; Sutton Publishing Limited, 1993</li>
|
||||
<li>Keppie, Lawrence. <strong>The Making of the Roman Army: From Republic to Empire</strong>: Oklahoma: University of Oklahoma Press, 1998</li>
|
||||
<li>Kern, Paul Bentley. <strong>Ancient Siege Warfare</strong>: Bloomington, Indiana: Indiana University Press, 1999</li>
|
||||
<li>Kiley, Kevin F. <strong>An Illustrated Encyclopedia of the Uniforms of the Roman World</strong>: Dayton, Ohio: Lorenz Books., 2019</li>
|
||||
<li>Le Bohec, Yann. <strong>The Imperial Roman Army</strong>: New York: Routledge, 2001</li>
|
||||
<li>Parker, H.M.D. <strong>The Roman Legions</strong>: Dorset Press, N.Y. (reprint of [1928] 1957 second edition); 1992</li>
|
||||
<li>Peterson, Daniel. <strong>The Roman Legions Recreated in Colour Photographs</strong>: Wiltshire: The Crowood Press Ltd., 2001</li>
|
||||
<li>Matyszak, Philip. <strong>Legionary: The Roman Soldier's (unofficial) Manual</strong>: London: Thames & Hudson Ltd., 2009</li>
|
||||
<li>May, Elmer, Stadler, Gerald P., Votaw, John F. (Department of History, United States Military Academy, West Point, New York). <strong>Ancient & Medieval Warfare</strong>: Wayne, N.J.: Avery Publishing Group Inc., 1984</li>
|
||||
<li>Pollard, Nigel & Berry, Joanne. <strong>The Complete Roman Legions</strong>: London: Thames & Hudson, 2012</li>
|
||||
<li>Roth, Jonathan P. <strong>Roman Warfare</strong>: New York: Cambridge University Press, 2009</li>
|
||||
<li>Scarre, Chris. <strong>Chronicle of The Roman Emperors</strong>: London: Thames & Hudson Ltd., 1995</li>
|
||||
<li>Shirley, Elizabeth. <strong>Building a Roman Legionary Fortress</strong>: Charleston S.C.: Arcadia Publishing Inc. (a Division of Tempus), 2001</li>
|
||||
<li>Simkins, Michael. <strong>Warriors of Rome</strong>: London: Blandford, 1988</li>
|
||||
<li>Southern, Pat. <strong>The Roman Army, A Social & Institutional History</strong>: New York: Oxford University Press, 2006</li>
|
||||
<li>Watson, G.R. <strong>The Roman Soldier</strong>: New York: Cornell University Press, 1985</li>
|
||||
<li>Wary, John. <strong>Warfare in the Classical World</strong>: Norman, Oklahoma: University of Oklahoma Press, 1995</li>
|
||||
<li>Webster, Graham. <strong>The Imperial Roman Army (of the First and Second Centuries A.D.)</strong>: Norman, Oklahoma: University of Oklahoma Press, 1998 (3rd ed.)</li>
|
||||
<li>Woolliscroft, D.I. <strong>Roman Military Signalling</strong>: Charleston: Tempus Publishing Inc., 2001</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="mb-2">Historical</h2>
|
||||
<h3 class="mb-2">Accounts</h3>
|
||||
<ul class="mt-0 extra-spacing">
|
||||
<li>
|
||||
Titus Livius (AKA Livy). <strong>Ab Urbe Condita</strong>: 753 - 9 BC<br>
|
||||
History of ancient Rome and includes the cities founding legends, the expulsion of kings and the reign of Augustus.
|
||||
</li>
|
||||
<li>
|
||||
Polybius. <strong>Historiae</strong>: 264 - 146 BC<br>
|
||||
History of the Roman Republic including the Punic wars & subjugation of Greece.
|
||||
</li>
|
||||
<li>
|
||||
Appian of Alexandria. <strong>Romaica</strong>: Circa 3rd Century BC - 43 BC<br>
|
||||
What remains recounts the foreign wars from the republic and the civil wars up to the Second Triumvirate
|
||||
</li>
|
||||
<li>
|
||||
Flavius Josephus. <strong>Bellum Iudaicum</strong>: 168 BC - 70 AD<br>
|
||||
Summarized history of the Jewish wars including the first Jewish Revolt.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar. <strong>De Bello Gallico</strong> (AKA <strong>Bellum Gallicum</strong>): 58 - 50 BC<br>
|
||||
First hand account of the Gallic wars.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar. <strong>De Bello Civili</strong> (AKA <strong>Bellum Civile</strong>): 49 - 48 BC<br>
|
||||
A first hand account of Caesar's civil war against Pompey the Great & the Roman Senate.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar (?). <strong>De Bello Alexandrino</strong> (AKA <strong>Bellum Alexandrinum</strong>): 47 - 46 BC<br>
|
||||
Continuation of Caesar's civil war while in Alexandria (Egypt) & Asia.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar (?). <strong>De Bello Africo</strong> (AKA <strong>Bellum Africum</strong>): 46 BC<br>
|
||||
Continuation of Caesar's civil war while in the province of Africa.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar (?). <strong>De Bello Hispaniensi</strong> (AKA <strong>Bellum Hispaniense</strong>): 45 BC<br>
|
||||
Continuation and end of Caesar's civil war while in Iberian Peninsula.
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>Annales</strong>: 42 BC - 68 AD<br>
|
||||
The history of Rome, from Tiberius to Nero<br>
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>Historiae</strong>: 42 - 96 AD<br>
|
||||
The history of Rome, from the downfall of nero to the death of Domitian (surviving documents span to Vespasian's reign)
|
||||
</li>
|
||||
<li>
|
||||
Flavius Arrianus Xenophon (AKA Arrian of Nicomedia). 85 - 190 AD<br>
|
||||
Several works, including the history of Alexander the Great, various military campaigns, notes on the philosopher Epictetus, & guides on hunting, navigation & cavalry
|
||||
</li>
|
||||
<li>
|
||||
Ammianus Marcellinus. <strong>Restrum Gestarum Libri</strong> (AKA <strong>Res gestae</strong>): 96 - 378 AD<br>
|
||||
The history of Rome from the ascension of Nerva to the death of Valens from (Only 353 - 378 AD survive)
|
||||
</li>
|
||||
<li>
|
||||
Lucius Mestrius Plutarchus (AKA Plutarch). 50 AD - 120 AD<br>
|
||||
Wrote over 50 biographies including those of Alexander the Great, Caesar & several emperors
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>De Vita Julii Agricolae</strong>: 77 - 84 AD<br>
|
||||
Recounts the the life of Gnaeus Jilius Agricola, a Roman governor of Britain.
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>De Origine et situ Germanorum</strong> (AKA <strong>Germania</strong>): 98 AD<br>
|
||||
The history of the Germanic people outside of the Roman Empire
|
||||
</li>
|
||||
<li>
|
||||
Lucius Cassius Dio (AKA Dio Cassius). <strong>Historia Romanae</strong>: 155 AD - 235 AD<br>
|
||||
80 volume history of Rome from it's foundation to the 3rd century
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="mb-2">Manuals</h3>
|
||||
<ul class="mt-0 extra-spacing">
|
||||
<li>
|
||||
Flavius Vegetius Renatus. <strong>Epitoma Rei Militaris</strong> (AKA <strong>De Re Militari</strong>): Circa 4th - 5th Century<br>
|
||||
Summary of historic military manuals
|
||||
</li>
|
||||
<li>
|
||||
Flavius Mauricius Tiberius Augustus. <strong>Strategikon</strong>: 539 AD - 602 AD<br>
|
||||
Comprehensive handbook & manual on war
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Hyginus (?). <strong>De Munitionibus Castrorum</strong> (AKA <strong>De Metatione Castrorum</strong>): Circa 1st - 4th century<br>
|
||||
Collection of military camp layouts
|
||||
</li>
|
||||
<li>
|
||||
Sextus Julius Frontinus. <strong>Stratagemata</strong>: Circa late 1st Century<br>
|
||||
Collection of historic examples of Greek & Roman military stratagems
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex p-3 pe-4 border rounded-1 bg-light align-items-center mt-4" style="max-width: min(100%, 550px)">
|
||||
<div class="pe-3">
|
||||
<img src="/assets/img/quote.png" alt="Info icon" style="height: 75px; width: auto">
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-1">Special Thanks</h3>
|
||||
<p class="m-0">Special thanks to our <em>Tribunus angusticlavius</em> (Staff Officer),<br>David Blain for providing sources</p>
|
||||
<a href="https://principialegionis.org/index.php/the-roman-army/resources/" target="_blank">More Info</a> / <a href="https://principialegionis.org" target="_blank">https://principialegionis.org</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="invert">
|
||||
<div class="banner d-print-none"></div>
|
||||
<div class="cap-width py-5 px-3">
|
||||
<div>
|
||||
<div class="d-flex align-items-end justify-content-between mb-2">
|
||||
<h1 class="mb-0">Resources</h1>
|
||||
<a class="d-none d-print-none d-sm-flex justify-content-center text-muted" href="javascript:window.print()">
|
||||
<mat-icon class="me-1">print</mat-icon>
|
||||
Print
|
||||
</a>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<br>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="mb-2">Websites</h2>
|
||||
<ul class="mt-0">
|
||||
<li><a href="https://www.erminestreetguard.co.uk" target="_blank">The Ermine Street Guard (https://erminestreetguard.co.uk)</a></li>
|
||||
<li><a href="https://www.jstor.org/journal/jromanstudies">The Journal of Roman Studies (https://jstor.org)</a></li>
|
||||
<li><a href="https://www.larp.com/legioxx/hndbk.html" target="_blank">Legio XX Handbook (https://larp.com/legioxx)</a></li>
|
||||
<li><a href="https://principialegionis.org" target="_blank">Principia Legionis (https://principialegionis.org)</a></li>
|
||||
<li><a href="https://www.romanempire.net">Roman Empire Groups (https://romanempire.net)</a></li>
|
||||
<li><a href="https://www.romansociety.org">The Roman Society (https://romansociety.org)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="mb-2">Modern Literature</h2>
|
||||
<h3 class="mb-2">Life</h3>
|
||||
<ul>
|
||||
<li>Adkins, Lesley and Adkins, Roy A. <strong>Handbook to Life in Ancient Rome</strong>: New York: Oxford University Press, 1994</li>
|
||||
<li>Connolly, Peter and Dodge, Hazel. <strong>The Ancient City – Life in Classical Athens & Rome</strong>: Oxford: Oxford University Press, 2001</li>
|
||||
<li>Harlow, Mary and Laurence, Ray. <strong>Growing Up and Growing Old in Ancient Rome</strong>: London; Routlage, 2002</li>
|
||||
<li>Shelton, Jo-Ann. <strong>As the Romans Did</strong>: New York: Oxford University Press; 1998 (2nd ed.)</li>
|
||||
</ul>
|
||||
<h3 class="mb-2">Warfare</h3>
|
||||
<ul class="mt-0">
|
||||
<li>Bishop, M.C. & Coulston, J.C.N. <strong>Roman Military Equipment</strong>: Oxford: Oxbow Books, 2006</li>
|
||||
<li>Breeze, David J. <strong>The Frontiers of Imperial Rome</strong>: South Yorkshire: Pen & Sword Books Ltd., 2011</li>
|
||||
<li>Gilliver, Kate, Goldsworthy, Adrian, Whitby, Michael. <strong>Rome at War (Caesar and his Legacy)</strong>: Oxford: Osprey Publishing, 2005</li>
|
||||
<li>Goldsworthy, Adrian. <strong>The Complete Roman Army</strong>: London: Thames & Hudson Ltd., 2003</li>
|
||||
<li>Goldsworthy, Adrian. <strong>Roman Warfare</strong>: London: Cassell & Co., 2000</li>
|
||||
<li>Hyland, Ann. <strong>Training the Roman Cavalry (From Arrian’s Ars Tactica)</strong>: Gloucestershire; Sutton Publishing Limited, 1993</li>
|
||||
<li>Keppie, Lawrence. <strong>The Making of the Roman Army: From Republic to Empire</strong>: Oklahoma: University of Oklahoma Press, 1998</li>
|
||||
<li>Kern, Paul Bentley. <strong>Ancient Siege Warfare</strong>: Bloomington, Indiana: Indiana University Press, 1999</li>
|
||||
<li>Kiley, Kevin F. <strong>An Illustrated Encyclopedia of the Uniforms of the Roman World</strong>: Dayton, Ohio: Lorenz Books., 2019</li>
|
||||
<li>Le Bohec, Yann. <strong>The Imperial Roman Army</strong>: New York: Routledge, 2001</li>
|
||||
<li>Parker, H.M.D. <strong>The Roman Legions</strong>: Dorset Press, N.Y. (reprint of [1928] 1957 second edition); 1992</li>
|
||||
<li>Peterson, Daniel. <strong>The Roman Legions Recreated in Colour Photographs</strong>: Wiltshire: The Crowood Press Ltd., 2001</li>
|
||||
<li>Matyszak, Philip. <strong>Legionary: The Roman Soldier's (unofficial) Manual</strong>: London: Thames & Hudson Ltd., 2009</li>
|
||||
<li>May, Elmer, Stadler, Gerald P., Votaw, John F. (Department of History, United States Military Academy, West Point, New York). <strong>Ancient & Medieval Warfare</strong>: Wayne, N.J.: Avery Publishing Group Inc., 1984</li>
|
||||
<li>Pollard, Nigel & Berry, Joanne. <strong>The Complete Roman Legions</strong>: London: Thames & Hudson, 2012</li>
|
||||
<li>Roth, Jonathan P. <strong>Roman Warfare</strong>: New York: Cambridge University Press, 2009</li>
|
||||
<li>Scarre, Chris. <strong>Chronicle of The Roman Emperors</strong>: London: Thames & Hudson Ltd., 1995</li>
|
||||
<li>Shirley, Elizabeth. <strong>Building a Roman Legionary Fortress</strong>: Charleston S.C.: Arcadia Publishing Inc. (a Division of Tempus), 2001</li>
|
||||
<li>Simkins, Michael. <strong>Warriors of Rome</strong>: London: Blandford, 1988</li>
|
||||
<li>Southern, Pat. <strong>The Roman Army, A Social & Institutional History</strong>: New York: Oxford University Press, 2006</li>
|
||||
<li>Watson, G.R. <strong>The Roman Soldier</strong>: New York: Cornell University Press, 1985</li>
|
||||
<li>Wary, John. <strong>Warfare in the Classical World</strong>: Norman, Oklahoma: University of Oklahoma Press, 1995</li>
|
||||
<li>Webster, Graham. <strong>The Imperial Roman Army (of the First and Second Centuries A.D.)</strong>: Norman, Oklahoma: University of Oklahoma Press, 1998 (3rd ed.)</li>
|
||||
<li>Woolliscroft, D.I. <strong>Roman Military Signalling</strong>: Charleston: Tempus Publishing Inc., 2001</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="mb-2">Historical Literature</h2>
|
||||
<h3 class="mb-2">Accounts</h3>
|
||||
<ul class="mt-0 extra-spacing">
|
||||
<li>
|
||||
Titus Livius (AKA Livy). <strong>Ab Urbe Condita</strong>: 753 - 9 BC<br>
|
||||
History of ancient Rome and includes the cities founding legends, the expulsion of kings and the reign of Augustus.
|
||||
</li>
|
||||
<li>
|
||||
Polybius. <strong>Historiae</strong>: 264 - 146 BC<br>
|
||||
History of the Roman Republic including the Punic wars & subjugation of Greece.
|
||||
</li>
|
||||
<li>
|
||||
Appian of Alexandria. <strong>Romaica</strong>: Circa 3rd Century BC - 43 BC<br>
|
||||
What remains recounts the foreign wars from the republic and the civil wars up to the Second Triumvirate
|
||||
</li>
|
||||
<li>
|
||||
Flavius Josephus. <strong>Bellum Iudaicum</strong>: 168 BC - 70 AD<br>
|
||||
Summarized history of the Jewish wars including the first Jewish Revolt.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar. <strong>De Bello Gallico</strong> (AKA <strong>Bellum Gallicum</strong>): 58 - 50 BC<br>
|
||||
First hand account of the Gallic wars.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar. <strong>De Bello Civili</strong> (AKA <strong>Bellum Civile</strong>): 49 - 48 BC<br>
|
||||
A first hand account of Caesar's civil war against Pompey the Great & the Roman Senate.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar (?). <strong>De Bello Alexandrino</strong> (AKA <strong>Bellum Alexandrinum</strong>): 47 - 46 BC<br>
|
||||
Continuation of Caesar's civil war while in Alexandria (Egypt) & Asia.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar (?). <strong>De Bello Africo</strong> (AKA <strong>Bellum Africum</strong>): 46 BC<br>
|
||||
Continuation of Caesar's civil war while in the province of Africa.
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Caesar (?). <strong>De Bello Hispaniensi</strong> (AKA <strong>Bellum Hispaniense</strong>): 45 BC<br>
|
||||
Continuation and end of Caesar's civil war while in Iberian Peninsula.
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>Annales</strong>: 42 BC - 68 AD<br>
|
||||
The history of Rome, from Tiberius to Nero<br>
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>Historiae</strong>: 42 - 96 AD<br>
|
||||
The history of Rome, from the downfall of nero to the death of Domitian (surviving documents span to Vespasian's reign)
|
||||
</li>
|
||||
<li>
|
||||
Flavius Arrianus Xenophon (AKA Arrian of Nicomedia). 85 - 190 AD<br>
|
||||
Several works, including the history of Alexander the Great, various military campaigns, notes on the philosopher Epictetus, & guides on hunting, navigation & cavalry
|
||||
</li>
|
||||
<li>
|
||||
Ammianus Marcellinus. <strong>Restrum Gestarum Libri</strong> (AKA <strong>Res gestae</strong>): 96 - 378 AD<br>
|
||||
The history of Rome from the ascension of Nerva to the death of Valens from (Only 353 - 378 AD survive)
|
||||
</li>
|
||||
<li>
|
||||
Lucius Mestrius Plutarchus (AKA Plutarch). 50 AD - 120 AD<br>
|
||||
Wrote over 50 biographies including those of Alexander the Great, Caesar & several emperors
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>De Vita Julii Agricolae</strong>: 77 - 84 AD<br>
|
||||
Recounts the the life of Gnaeus Jilius Agricola, a Roman governor of Britain.
|
||||
</li>
|
||||
<li>
|
||||
Publius Cornelius Tacitus. <strong>De Origine et situ Germanorum</strong> (AKA <strong>Germania</strong>): 98 AD<br>
|
||||
The history of the Germanic people outside of the Roman Empire
|
||||
</li>
|
||||
<li>
|
||||
Lucius Cassius Dio (AKA Dio Cassius). <strong>Historia Romanae</strong>: 155 AD - 235 AD<br>
|
||||
80 volume history of Rome from it's foundation to the 3rd century
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="mb-2">Manuals</h3>
|
||||
<ul class="mt-0 extra-spacing">
|
||||
<li>
|
||||
Flavius Vegetius Renatus. <strong>Epitoma Rei Militaris</strong> (AKA <strong>De Re Militari</strong>): Circa 4th - 5th Century<br>
|
||||
Summary of historic military manuals
|
||||
</li>
|
||||
<li>
|
||||
Flavius Mauricius Tiberius Augustus. <strong>Strategikon</strong>: 539 AD - 602 AD<br>
|
||||
Comprehensive handbook & manual on war
|
||||
</li>
|
||||
<li>
|
||||
Gaius Julius Hyginus (?). <strong>De Munitionibus Castrorum</strong> (AKA <strong>De Metatione Castrorum</strong>): Circa 1st - 4th century<br>
|
||||
Collection of military camp layouts
|
||||
</li>
|
||||
<li>
|
||||
Sextus Julius Frontinus. <strong>Stratagemata</strong>: Circa late 1st Century<br>
|
||||
Collection of historic examples of Greek & Roman military stratagems
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex p-3 pe-4 border rounded-1 bg-light align-items-center mt-4" style="max-width: min(100%, 550px)">
|
||||
<div class="pe-3">
|
||||
<img src="/assets/img/quote.png" alt="Info icon" style="height: 75px; width: auto">
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-1">Special Thanks</h3>
|
||||
<p class="m-0">Special thanks to our <em>Tribunus angusticlavius</em> (Staff Officer),<br>David Blain for providing sources</p>
|
||||
<a href="https://principialegionis.org/index.php/the-roman-army/resources/" target="_blank">More Info</a> / <a href="https://principialegionis.org" target="_blank">https://principialegionis.org</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
BIN
src/assets/img/calendar-banner.png
Normal file
BIN
src/assets/img/calendar-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -3,27 +3,19 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>LEGIO · XXX</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="author" content="Zak Timson">
|
||||
<meta property="og:title" content="LEGIO · XXX">
|
||||
<meta property="og:site_name" content="LEGIO · XXX">
|
||||
<meta property="og:type" content="article"/>
|
||||
<meta property="og:url" content="https://legioxxx.zakscode.com">
|
||||
<meta property="og:image" content="https://legioxxx.zakscode.com/assets/img/standard.png">
|
||||
<meta name="twitter:card" content="https://legioxxx.zakscode.com/assets/img/standard.png">
|
||||
<meta name="twitter:image:alt" content="Alt text for image">
|
||||
<meta name="description" content="Legio XXX is a North American Roman reenactment group established in 2004. It's members represent a cross between living history enthusiasts and 'edutainers' that recreate the lives of soldiers found in Trajan's leagions during the 1st - 2nd Century AD">
|
||||
<meta property="og:description" content="Legio XXX is a North American Roman reenactment group established in 2004. It's members represent a cross between living history enthusiasts and 'edutainers' that recreate the lives of soldiers found in Trajan's leagions during the 1st - 2nd Century AD">
|
||||
|
||||
<title>LEGIO · XXX</title>
|
||||
<!-- Momentum:meta -->
|
||||
|
||||
<link href="assets/img/favicon.svg" rel="icon" type="image/svg">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://use.fontawesome.com/releases/v6.1.1/css/all.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
|
||||
<!-- Momentum:theme -->
|
||||
</head>
|
||||
|
||||
<body class="mat-typography" style="background: #000">
|
||||
|
||||
@@ -111,3 +111,10 @@ ol {
|
||||
a, a:visited { color: #fff; }
|
||||
a:hover, a:visited:hover { color: rgba(255, 255, 255, 0.8); }
|
||||
}
|
||||
|
||||
@media print {
|
||||
.event-card {
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./out-tsc/app",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
@@ -13,6 +15,7 @@
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
|
||||
Reference in New Issue
Block a user