mirror of
https://github.com/FoggedLens/deflock.git
synced 2026-02-12 15:02:45 +00:00
add chapters, no exposure
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<v-row
|
||||
justify="center"
|
||||
class="hero text-center mb-4"
|
||||
:class="{ 'hero-image': imageUrl, 'hero-gradient': gradient }"
|
||||
:class="{ 'hero-image': imageUrl }"
|
||||
:style="heroStyle"
|
||||
>
|
||||
<v-col cols="12" md="8">
|
||||
@@ -33,7 +33,10 @@ const props = defineProps({
|
||||
title: String,
|
||||
description: String,
|
||||
imageUrl: String,
|
||||
gradient: String,
|
||||
gradient: {
|
||||
type: String,
|
||||
default: 'linear-gradient(135deg, rgb(var(--v-theme-primary)) 0%, rgb(var(--v-theme-secondary)) 100%)',
|
||||
},
|
||||
buttonText: String,
|
||||
buttonTo: String,
|
||||
buttonHref: String,
|
||||
@@ -51,14 +54,11 @@ const target = computed(() =>
|
||||
props.buttonHref && !props.buttonHref.startsWith('#') ? '_blank' : '_self'
|
||||
);
|
||||
|
||||
const heroStyle = computed(() => {
|
||||
if (props.gradient) {
|
||||
return `background: ${props.gradient};`;
|
||||
} else if (props.imageUrl) {
|
||||
return `background: url('${props.imageUrl}') no-repeat ${props.backgroundPosition} / cover; --hero-opacity: ${props.opacity};`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
const heroStyle = computed(() => (
|
||||
props.imageUrl ?
|
||||
`background: url('${props.imageUrl}') no-repeat ${props.backgroundPosition} / cover; --hero-opacity: ${props.opacity};` :
|
||||
`background: ${props.gradient};`
|
||||
));
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -31,6 +31,14 @@ const router = createRouter({
|
||||
title: 'ALPR Map | DeFlock'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/chapters',
|
||||
name: 'chapters',
|
||||
component: () => import('../views/Chapters.vue'),
|
||||
meta: {
|
||||
title: 'DeFlock Chapters | Connect Locally'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
|
||||
36
webapp/src/services/cmsService.ts
Normal file
36
webapp/src/services/cmsService.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface Chapter {
|
||||
id: string;
|
||||
name: string;
|
||||
city: string;
|
||||
state: string;
|
||||
website: string;
|
||||
}
|
||||
|
||||
export interface ChaptersResponse {
|
||||
data: Chapter[];
|
||||
meta?: {
|
||||
total_count: number;
|
||||
filter_count: number;
|
||||
};
|
||||
}
|
||||
|
||||
const CMS_BASE_URL = "https://cms.deflock.me";
|
||||
|
||||
const cmsApiService = axios.create({
|
||||
baseURL: CMS_BASE_URL,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
export const cmsService = {
|
||||
async getChapters(): Promise<Chapter[]> {
|
||||
try {
|
||||
const response = await cmsApiService.get("/items/chapters?sort=name");
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching chapters:", error);
|
||||
throw new Error("Failed to fetch chapters");
|
||||
}
|
||||
}
|
||||
};
|
||||
171
webapp/src/views/Chapters.vue
Normal file
171
webapp/src/views/Chapters.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<DefaultLayout>
|
||||
<template #header>
|
||||
<Hero
|
||||
title="Local Groups"
|
||||
description="Find and join an independent group fighting mass surveillance near you."
|
||||
/>
|
||||
</template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" class="mx-auto text-center">
|
||||
<p>
|
||||
These groups are <b>independently run</b> and are <b>not affiliated with DeFlock</b>.
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" class="mx-auto">
|
||||
<div v-if="chapters.length">
|
||||
<div v-for="(state, idx) in statesWithChapters" :key="state" class="mb-8">
|
||||
<h3 class="font-weight-bold text-h6 mb-0">{{ abbrevToState[state] }}</h3>
|
||||
<v-divider class="mb-4" />
|
||||
<v-list>
|
||||
<template v-for="(chapter, idx) in chaptersByState[state]" :key="chapter.id">
|
||||
<v-list-item
|
||||
two-line
|
||||
class="chapter-list-item"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="font-weight-bold">
|
||||
{{ chapter.name }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle class="font-weight-bold">
|
||||
{{ chapter.city }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
|
||||
<template #append>
|
||||
<v-list-item-action>
|
||||
<v-btn :href="chapter.website" target="_blank" rel="noopener" color="primary" variant="outlined" size="small">
|
||||
Visit Website
|
||||
<v-icon end>mdi-open-in-new</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</template>
|
||||
|
||||
</v-list-item>
|
||||
<v-divider v-if="idx < chaptersByState[state].length - 1" />
|
||||
</template>
|
||||
</v-list>
|
||||
</div>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<div class="text-center">
|
||||
<p>Don't see a group near you?</p>
|
||||
<v-btn color="primary" variant="text" to="/contact">
|
||||
<v-icon start>mdi-plus</v-icon>Submit a Group
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center">
|
||||
<div v-if="!isLoading">
|
||||
<v-alert variant="tonal" color="info">No chapters found.</v-alert>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-progress-circular indeterminate color="primary" size="48" />
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</DefaultLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.vue'
|
||||
import Hero from '@/components/layout/Hero.vue'
|
||||
import { cmsService } from '@/services/cmsService'
|
||||
|
||||
interface Chapter {
|
||||
id: string
|
||||
name: string
|
||||
city: string
|
||||
state: string
|
||||
website: string
|
||||
}
|
||||
|
||||
const abbrevToState: Record<string, string> = {
|
||||
"AL": "Alabama",
|
||||
"AK": "Alaska",
|
||||
"AZ": "Arizona",
|
||||
"AR": "Arkansas",
|
||||
"CA": "California",
|
||||
"CO": "Colorado",
|
||||
"CT": "Connecticut",
|
||||
"DE": "Delaware",
|
||||
"DC": "District Of Columbia",
|
||||
"FL": "Florida",
|
||||
"GA": "Georgia",
|
||||
"HI": "Hawaii",
|
||||
"ID": "Idaho",
|
||||
"IL": "Illinois",
|
||||
"IN": "Indiana",
|
||||
"IA": "Iowa",
|
||||
"KS": "Kansas",
|
||||
"KY": "Kentucky",
|
||||
"LA": "Louisiana",
|
||||
"ME": "Maine",
|
||||
"MD": "Maryland",
|
||||
"MA": "Massachusetts",
|
||||
"MI": "Michigan",
|
||||
"MN": "Minnesota",
|
||||
"MS": "Mississippi",
|
||||
"MO": "Missouri",
|
||||
"MT": "Montana",
|
||||
"NE": "Nebraska",
|
||||
"NV": "Nevada",
|
||||
"NH": "New Hampshire",
|
||||
"NJ": "New Jersey",
|
||||
"NM": "New Mexico",
|
||||
"NY": "New York",
|
||||
"NC": "North Carolina",
|
||||
"ND": "North Dakota",
|
||||
"OH": "Ohio",
|
||||
"OK": "Oklahoma",
|
||||
"OR": "Oregon",
|
||||
"PA": "Pennsylvania",
|
||||
"RI": "Rhode Island",
|
||||
"SC": "South Carolina",
|
||||
"SD": "South Dakota",
|
||||
"TN": "Tennessee",
|
||||
"TX": "Texas",
|
||||
"UT": "Utah",
|
||||
"VT": "Vermont",
|
||||
"VA": "Virginia",
|
||||
"WA": "Washington",
|
||||
"WV": "West Virginia",
|
||||
"WI": "Wisconsin",
|
||||
"WY": "Wyoming"
|
||||
}
|
||||
|
||||
const chapters = ref<Chapter[]>([])
|
||||
const isLoading = ref(true)
|
||||
|
||||
const chaptersByState = computed(() => {
|
||||
const grouped: Record<string, Chapter[]> = {}
|
||||
chapters.value.forEach(chapter => {
|
||||
if (!grouped[chapter.state]) grouped[chapter.state] = []
|
||||
grouped[chapter.state].push(chapter)
|
||||
})
|
||||
return grouped
|
||||
})
|
||||
|
||||
const statesWithChapters = computed(() => {
|
||||
return Object.keys(chaptersByState.value).sort()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
cmsService.getChapters()
|
||||
.then(data => {
|
||||
chapters.value = data
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -29,28 +29,6 @@
|
||||
|
||||
<!-- Dangers Section -->
|
||||
<v-container class="pb-10 text-center info-section">
|
||||
<!-- Flock Warning Alert -->
|
||||
<v-alert
|
||||
type="warning"
|
||||
variant="tonal"
|
||||
class="text-left mx-4 mt-4"
|
||||
prominent
|
||||
border="start"
|
||||
closable
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-alert</v-icon>
|
||||
</template>
|
||||
<v-alert-title class="text-h6 font-weight-bold">
|
||||
Similar Sites are Disappearing
|
||||
</v-alert-title>
|
||||
<div>
|
||||
<p>
|
||||
Following <a target="_blank" href="https://haveibeenflocked.com/news/cyble-downtime">takedown claims submitted on behalf of Flock</a>, sites similar to DeFlock have gone offline. If DeFlock disappears, it's clear why.
|
||||
</p>
|
||||
</div>
|
||||
</v-alert>
|
||||
|
||||
<h2 class="mb-4">What are ALPRs</h2>
|
||||
|
||||
<p class="text-left px-6">
|
||||
|
||||
Reference in New Issue
Block a user