mirror of
https://github.com/FoggedLens/deflock.git
synced 2026-02-12 15:02:45 +00:00
various cleanup
This commit is contained in:
15
README.md
15
README.md
@@ -14,24 +14,24 @@ I created this project after noticing the mass deployment of ALPRs in cities, to
|
|||||||
Uses OpenStreetMap data to populate a map with crowdsourced locations of ALPRs, along with their type and direction they face.
|
Uses OpenStreetMap data to populate a map with crowdsourced locations of ALPRs, along with their type and direction they face.
|
||||||
|
|
||||||
### Report ALPRs
|
### Report ALPRs
|
||||||
Provides OSM tags for easy reporting of ALPRs based on brand on OSM's editing site. Evemtually, this will be a native feature of the site.
|
Provides OSM tags for easy reporting of ALPRs based on brand on OSM's editing site. Eventually, this will be a native feature of the site.
|
||||||
|
|
||||||
### Learn About ALPRs
|
### Learn About ALPRs
|
||||||
See photos of common ALPRs and learn about their capabilities.
|
See photos of common ALPRs and learn about their capabilities.
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
_Likely to change soon._
|
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
* Scala
|
* Scala
|
||||||
* PekkoHTTP
|
* PekkoHTTP
|
||||||
* Nginx
|
* Nginx
|
||||||
|
|
||||||
### Cloud
|
### Cloud
|
||||||
* AWS Lambda (for [clustering](serverless/alpr_clusters) and [counts](serverless/alpr_counts))
|
* AWS Lambda (for [region segmenting](serverless/alpr_clusters) and [counts](serverless/alpr_counts))
|
||||||
* AWS S3
|
* AWS S3
|
||||||
* AWS ECR
|
* AWS ECR
|
||||||
|
* Cloudflare as DNS + Proxy
|
||||||
|
* Directus CDN
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
* Vue3
|
* Vue3
|
||||||
@@ -39,9 +39,8 @@ _Likely to change soon._
|
|||||||
* Vue Leaflet (mapping library)
|
* Vue Leaflet (mapping library)
|
||||||
|
|
||||||
### Services
|
### Services
|
||||||
* OpenStreetMap - Overpass API
|
* OpenStreetMap - Overpass API, Basic Map Tiles
|
||||||
* Nominatim - Geocoding
|
* Nominatim - Geocoding
|
||||||
* Stadia Maps - Map Tiles
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -60,6 +59,10 @@ _Likely to change soon._
|
|||||||
1. `cd shotgun`
|
1. `cd shotgun`
|
||||||
2. `sbt run`
|
2. `sbt run`
|
||||||
|
|
||||||
|
### Building for Production
|
||||||
|
|
||||||
|
See [Dockerfile](./Dockerfile).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We welcome contributions from anyone. Here's how you can help:
|
We welcome contributions from anyone. Here's how you can help:
|
||||||
|
|||||||
BIN
webapp/public/donate.webp
Normal file
BIN
webapp/public/donate.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 313 KiB |
@@ -1,44 +1,169 @@
|
|||||||
|
/* Modern CSS Custom Properties */
|
||||||
|
:root {
|
||||||
|
--df-background-color: white;
|
||||||
|
--df-blue: rgb(18, 151, 195);
|
||||||
|
|
||||||
|
/* Modern Typography Scale */
|
||||||
|
--font-size-base: 1rem;
|
||||||
|
--font-size-lg: 1.125rem;
|
||||||
|
--font-size-xl: 1.25rem;
|
||||||
|
--font-size-2xl: 1.5rem;
|
||||||
|
|
||||||
|
/* Responsive font sizes */
|
||||||
|
--font-size-body: clamp(1rem, 2.5vw, 1.125rem);
|
||||||
|
--line-height-base: 1.6;
|
||||||
|
--line-height-relaxed: 1.75;
|
||||||
|
|
||||||
|
/* Spacing scale */
|
||||||
|
--space-xs: 0.5rem;
|
||||||
|
--space-sm: 0.75rem;
|
||||||
|
--space-md: 1rem;
|
||||||
|
--space-lg: 1.5rem;
|
||||||
|
--space-xl: 2rem;
|
||||||
|
--space-2xl: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improved base typography */
|
||||||
|
html {
|
||||||
|
font-size: 16px; /* Base font size for rem calculations */
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: var(--font-size-body);
|
||||||
|
line-height: var(--line-height-relaxed);
|
||||||
|
font-feature-settings: "kern" 1, "liga" 1;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern link styles */
|
||||||
a {
|
a {
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
color: var(--df-blue);
|
color: var(--df-blue);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: color-mix(in srgb, var(--df-blue) 80%, black);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* underline only simple <a> elements */
|
/* underline only simple <a> elements */
|
||||||
a:not([class]):hover {
|
a:not([class]):hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
text-decoration-thickness: 2px;
|
||||||
|
text-underline-offset: 2px;
|
||||||
:root {
|
|
||||||
--df-background-color: white;
|
|
||||||
--df-text-color: #ccc;
|
|
||||||
--df-blue: rgb(18, 151, 195);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaflet-popup-content-wrapper, .leaflet-popup-tip, .leaflet-bar a {
|
.leaflet-popup-content-wrapper, .leaflet-popup-tip, .leaflet-bar a {
|
||||||
background: var(--df-background-color) !important;
|
background: var(--df-background-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaflet-bar a {
|
|
||||||
color: var(--df-text-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marker-cluster span {
|
.marker-cluster span {
|
||||||
/* changing to dark mode should not affect the map yet */
|
/* changing to dark mode should not affect the map yet */
|
||||||
color: #000000 !important;
|
color: #000000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
/* Content elements use serif for better readability */
|
||||||
font-family: "PT Serif", serif;
|
p, li, blockquote, .content {
|
||||||
/* font-size: 1.1em; */
|
font-family: "PT Serif", Georgia, "Times New Roman", serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Modern paragraph styling */
|
||||||
|
p {
|
||||||
|
font-size: var(--font-size-body);
|
||||||
|
line-height: var(--line-height-relaxed);
|
||||||
|
margin: var(--space-md) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive headings */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: var(--space-xl);
|
||||||
|
margin-bottom: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce heading top margins in Vuetify components */
|
||||||
|
.v-card h1, .v-card h2, .v-card h3, .v-card h4, .v-card h5, .v-card h6, .v-card p,
|
||||||
|
.v-dialog h1, .v-dialog h2, .v-dialog h3, .v-dialog h4, .v-dialog h5, .v-dialog h6, .v-dialog p,
|
||||||
|
.v-sheet h1, .v-sheet h2, .v-sheet h3, .v-sheet h4, .v-sheet h5, .v-sheet h6, .v-sheet p,
|
||||||
|
.v-toolbar h1, .v-toolbar h2, .v-toolbar h3, .v-toolbar h4, .v-toolbar h5, .v-toolbar h6, .v-toolbar p,
|
||||||
|
.v-expansion-panel h1, .v-expansion-panel h2, .v-expansion-panel h3, .v-expansion-panel h4, .v-expansion-panel h5, .v-expansion-panel h6, .v-expansion-panel p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: clamp(1.75rem, 5vw, 3rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: clamp(1.5rem, 4vw, 2.25rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: clamp(1.25rem, 3vw, 1.875rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utility classes */
|
||||||
.serif {
|
.serif {
|
||||||
font-family: "PT Serif", serif;
|
font-family: "PT Serif", Georgia, "Times New Roman", serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sans-serif {
|
.sans-serif {
|
||||||
font-family: unset !important;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improved list styling */
|
||||||
|
ul, ol {
|
||||||
|
margin: var(--space-md) 0;
|
||||||
|
padding-left: var(--space-lg);
|
||||||
|
line-height: var(--line-height-relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li, ol li {
|
||||||
|
margin: var(--space-sm) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Better text spacing for improved readability */
|
||||||
|
blockquote {
|
||||||
|
margin: var(--space-xl) 0;
|
||||||
|
padding: var(--space-md) var(--space-lg);
|
||||||
|
border-left: 4px solid var(--df-blue);
|
||||||
|
font-style: italic;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code styling */
|
||||||
|
code, pre {
|
||||||
|
font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace;
|
||||||
|
font-size: 0.875em;
|
||||||
|
background-color: rgb(33,33,33);
|
||||||
|
color: white;
|
||||||
|
padding: 0.125em 0.25em;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: var(--space-md);
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.narrow-text {
|
||||||
|
max-width: 85ch !important;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent Scrolling Horizontally */
|
/* Prevent Scrolling Horizontally */
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
/* Typography rules */
|
|
||||||
|
|
||||||
body {
|
|
||||||
line-height: 1.6;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
margin-top: 1.5em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.8em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul, ol {
|
|
||||||
margin: 1em 0;
|
|
||||||
padding-left: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li, ol li {
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps, defineEmits } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: boolean; discordUrl: string }>();
|
const props = defineProps<{ modelValue: boolean; discordUrl: string }>();
|
||||||
const emit = defineEmits(['update:modelValue', 'proceed']);
|
const emit = defineEmits(['update:modelValue', 'proceed']);
|
||||||
|
|
||||||
|
|||||||
@@ -50,15 +50,15 @@
|
|||||||
|
|
||||||
<!-- Copyright -->
|
<!-- Copyright -->
|
||||||
<v-col
|
<v-col
|
||||||
class="text-center serif copyright d-flex align-center justify-center text-grey-darken-1"
|
class="text-center d-flex align-center justify-center text-grey-darken-1"
|
||||||
cols="12"
|
cols="12"
|
||||||
sm="6"
|
sm="6"
|
||||||
>
|
>
|
||||||
<div>
|
<div class="copyright">
|
||||||
<p>© {{ currentYear }} DeFlock. All Rights Reserved</p>
|
<p>© {{ currentYear }} DeFlock. All Rights Reserved</p>
|
||||||
<p>Map data © <a href="https://www.openstreetmap.org/copyright" target="_blank" style="color: unset; font-weight: normal;">OpenStreetMap contributors</a></p>
|
<p>Map data © <a href="https://www.openstreetmap.org/copyright" target="_blank" style="color: unset; font-weight: normal;">OpenStreetMap contributors</a></p>
|
||||||
<p class="mt-4">v1.1.0</p>
|
<p class="mt-4">v1.1.0</p>
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
@@ -66,7 +66,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { icon } from 'leaflet';
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useTheme } from 'vuetify';
|
import { useTheme } from 'vuetify';
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -92,7 +91,8 @@ const externalLinks = [
|
|||||||
.custom-icon {
|
.custom-icon {
|
||||||
opacity: var(--v-medium-emphasis-opacity);
|
opacity: var(--v-medium-emphasis-opacity);
|
||||||
}
|
}
|
||||||
.copyright {
|
.copyright p {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
line-height: 0.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ const props = defineProps({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0.6,
|
default: 0.6,
|
||||||
},
|
},
|
||||||
|
backgroundPosition: {
|
||||||
|
type: String,
|
||||||
|
default: 'center center',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const target = computed(() =>
|
const target = computed(() =>
|
||||||
@@ -51,7 +55,7 @@ const heroStyle = computed(() => {
|
|||||||
if (props.gradient) {
|
if (props.gradient) {
|
||||||
return `background: ${props.gradient};`;
|
return `background: ${props.gradient};`;
|
||||||
} else if (props.imageUrl) {
|
} else if (props.imageUrl) {
|
||||||
return `background: url('${props.imageUrl}') no-repeat center center / cover; --hero-opacity: ${props.opacity};`;
|
return `background: url('${props.imageUrl}') no-repeat ${props.backgroundPosition} / cover; --hero-opacity: ${props.opacity};`;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
|
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<main class="mb-16">
|
<main :class="{ 'mb-16': !noBottomMargin }">
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -14,4 +14,11 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '../components/layout/Footer.vue'
|
import Footer from '../components/layout/Footer.vue'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
noBottomMargin: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -49,26 +49,19 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/report',
|
path: '/report',
|
||||||
name: 'reportChoose',
|
name: 'report',
|
||||||
component: () => import('../views/ReportBase.vue'),
|
component: () => import('../views/ReportChoose.vue'),
|
||||||
children: [
|
meta: {
|
||||||
{
|
title: 'Submit Cameras | DeFlock'
|
||||||
path: '',
|
}
|
||||||
name: 'report',
|
},
|
||||||
component: () => import('../views/ReportChoose.vue'),
|
{
|
||||||
meta: {
|
path: '/report/id',
|
||||||
title: 'Submit Cameras | DeFlock'
|
name: 'reportID',
|
||||||
},
|
component: () => import('../views/ReportID.vue'),
|
||||||
},
|
meta: {
|
||||||
{
|
title: 'Submit Cameras | DeFlock'
|
||||||
path: '/report/id',
|
}
|
||||||
name: 'reportID',
|
|
||||||
component: () => import('../views/ReportID.vue'),
|
|
||||||
meta: {
|
|
||||||
title: 'Submit Cameras | DeFlock'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/council',
|
path: '/council',
|
||||||
@@ -138,6 +131,9 @@ const router = createRouter({
|
|||||||
path: '/foia',
|
path: '/foia',
|
||||||
name: 'foia',
|
name: 'foia',
|
||||||
component: () => import('../views/FOIA.vue'),
|
component: () => import('../views/FOIA.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'How to Request Public Records | DeFlock'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/identify',
|
path: '/identify',
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container>
|
<DefaultLayout>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1>Page Not Found</h1>
|
<h1>Page Not Found</h1>
|
||||||
|
|
||||||
<div class="mt-8 mb-16">
|
<div class="mt-8 mb-16">
|
||||||
<v-img height="300" src="/404.webp" />
|
<v-img height="300" src="/404.webp" />
|
||||||
<p class="caption">just like the license plate this guy is searching for</p>
|
<p class="caption">... just like the license plate this guy is searching for.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-btn color="primary" to="/"><v-icon start>mdi-home</v-icon>Home</v-btn>
|
<v-btn color="primary" to="/"><v-icon start>mdi-home</v-icon>Home</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</v-container>
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.caption {
|
.caption {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
color: #666;
|
color: var(--v-theme-on-surface);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,86 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container max-width="1000" class="mb-16">
|
<DefaultLayout>
|
||||||
<h2>About Us</h2>
|
<v-container class="narrow-text">
|
||||||
<p>
|
<h2>About Us</h2>
|
||||||
Welcome to DeFlock, your go-to resource for understanding and addressing the growing presence of Automated License Plate Readers (ALPRs) in our communities.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Our Mission</h2>
|
|
||||||
<p>
|
|
||||||
Our mission is simple: to shine a light on the widespread use of ALPR technology, raise awareness about the threats it poses to personal privacy and civil liberties, and empower the public to take action. ALPRs are increasingly being deployed by law enforcement and private companies without the public's full understanding or consent, and they collect vast amounts of data on our movements, often without any real oversight.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Why This Matters</h2>
|
|
||||||
<p>
|
|
||||||
Privacy is a fundamental right, and the growing use of ALPRs threatens to erode it. Every day, ALPRs capture and store details about the movements of millions of innocent people, creating a massive database that can be used for anything from routine surveillance to potentially more nefarious purposes. This constant monitoring can deter people from exercising their freedoms, like attending protests or engaging in other forms of free expression, which should concern all of us.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>What We Do</h2>
|
|
||||||
<div class="ml-4">
|
|
||||||
<h3>Educate</h3>
|
|
||||||
<p>
|
<p>
|
||||||
We provide easy-to-understand information about what ALPRs are, how they work, and why you should care.
|
Welcome to DeFlock, your go-to resource for understanding and addressing the growing presence of Automated License Plate Readers (ALPRs) in our communities.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Report & Track</h3>
|
<h2>Our Mission</h2>
|
||||||
<p>
|
<p>
|
||||||
Through our platform, you can report locations where you spot ALPRs, and view an interactive map showing where these surveillance devices are deployed.
|
Our mission is simple: to shine a light on the widespread use of ALPR technology, raise awareness about the threats it poses to personal privacy and civil liberties, and empower the public to take action. ALPRs are increasingly being deployed by law enforcement and private companies without the public's full understanding or consent, and they collect vast amounts of data on our movements, often without any real oversight.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Advocate for Change</h3>
|
<h2>Why This Matters</h2>
|
||||||
<p>
|
<p>
|
||||||
We encourage communities to stand up against the unchecked spread of these surveillance systems. Together, we can push for greater accountability, stronger privacy protections, and a rollback of invasive surveillance practices.
|
Privacy is a fundamental right, and the growing use of ALPRs threatens to erode it. Every day, ALPRs capture and store details about the movements of millions of innocent people, creating a massive database that can be used for anything from routine surveillance to potentially more nefarious purposes. This constant monitoring can deter people from exercising their freedoms, like attending protests or engaging in other forms of free expression, which should concern all of us.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
|
<h2>What We Do</h2>
|
||||||
<h2>Get Involved</h2>
|
<div class="ml-4">
|
||||||
<p>
|
<h3>Educate</h3>
|
||||||
Want to help us in our mission? Here are a few ways you can get involved:
|
<p>
|
||||||
</p>
|
We provide easy-to-understand information about what ALPRs are, how they work, and why you should care.
|
||||||
<div class="ml-4">
|
</p>
|
||||||
<h3>Report ALPRs</h3>
|
|
||||||
|
<h3>Report & Track</h3>
|
||||||
|
<p>
|
||||||
|
Through our platform, you can report locations where you spot ALPRs, and view an interactive map showing where these surveillance devices are deployed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Advocate for Change</h3>
|
||||||
|
<p>
|
||||||
|
We encourage communities to stand up against the unchecked spread of these surveillance systems. Together, we can push for greater accountability, stronger privacy protections, and a rollback of invasive surveillance practices.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Get Involved</h2>
|
||||||
<p>
|
<p>
|
||||||
If you spot an ALPR in your community, <router-link to="/report">let us know</router-link>! Your reports help us build a more comprehensive picture of where these devices are being used.
|
Want to help us in our mission? Here are a few ways you can get involved:
|
||||||
</p>
|
</p>
|
||||||
|
<div class="ml-4">
|
||||||
<h3>Spread the Word</h3>
|
<h3>Report ALPRs</h3>
|
||||||
<p>
|
<p>
|
||||||
Share our site with your friends, family, and social networks to help raise awareness about the dangers of ALPRs.
|
If you spot an ALPR in your community, <router-link to="/report">let us know</router-link>! Your reports help us build a more comprehensive picture of where these devices are being used.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
</v-container>
|
<h3>Spread the Word</h3>
|
||||||
|
<p>
|
||||||
<Footer />
|
Share our site with your friends, family, and social networks to help raise awareness about the dangers of ALPRs.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* TODO: put this all in one place, also in what-is view */
|
|
||||||
h2 {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-top: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-image {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* as you can see, this one is the most updated */
|
|
||||||
a {
|
|
||||||
color: var(green);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="mb-16">
|
<DefaultLayout>
|
||||||
|
<v-container>
|
||||||
<v-row justify="center" class="mb-8">
|
<v-row justify="center" class="mb-8">
|
||||||
<v-col cols="12" md="8" class="text-center">
|
<v-col cols="12" md="8" class="text-center">
|
||||||
<h1 class="text-h3 font-weight-bold mb-4">Contact Us</h1>
|
<h1 class="text-h3 font-weight-bold mb-4">Contact Us</h1>
|
||||||
@@ -126,12 +127,11 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import { useTheme } from 'vuetify';
|
import { useTheme } from 'vuetify';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
@@ -141,8 +141,6 @@ const showContactOptions = ref(false);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
@import url('@/assets/typography.css');
|
|
||||||
|
|
||||||
.card-title-wrap {
|
.card-title-wrap {
|
||||||
white-space: normal !important;
|
white-space: normal !important;
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
|
|||||||
@@ -1,375 +1,377 @@
|
|||||||
<template>
|
<template>
|
||||||
<Hero
|
<DefaultLayout>
|
||||||
imageUrl="/city-council.jpg"
|
<template #header>
|
||||||
title="City Council"
|
<Hero
|
||||||
description="Block ALPRs in your community using the proven technique of petitioning your city or town's council."
|
imageUrl="/city-council.jpg"
|
||||||
/>
|
title="City Council"
|
||||||
|
description="Block ALPRs in your community using the proven technique of petitioning your city or town's council."
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container class="mb-16">
|
<v-container>
|
||||||
<!-- Introduction Section -->
|
<!-- Introduction Section -->
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
||||||
<div class="text-center mb-8">
|
<div class="text-center mb-8">
|
||||||
<v-icon size="64" color="var(--df-blue)" class="mb-4">mdi-account-voice</v-icon>
|
<v-icon size="64" color="var(--df-blue)" class="mb-4">mdi-account-voice</v-icon>
|
||||||
<h2 class="text-h4 mb-4 font-weight-bold">Your Voice Matters Locally</h2>
|
<h2 class="text-h4 mb-4 font-weight-bold">Your Voice Matters Locally</h2>
|
||||||
<p class="text-h6 text-medium-emphasis serif">
|
<p class="text-h6 text-medium-emphasis serif">
|
||||||
City council members rely on us to understand public opinion. Here's your step-by-step guide to effectively advocate
|
City council members rely on us to understand public opinion. Here's your step-by-step guide to effectively advocate
|
||||||
against mass surveillance systems with your local representatives.
|
against mass surveillance systems with your local representatives.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- Talking Points -->
|
<!-- Talking Points -->
|
||||||
<v-row class="mb-8">
|
<v-row class="mb-8">
|
||||||
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
||||||
<v-card
|
<v-card
|
||||||
class="pa-4"
|
class="pa-4"
|
||||||
elevation="2"
|
elevation="2"
|
||||||
rounded="lg"
|
rounded="lg"
|
||||||
hover
|
hover
|
||||||
href="https://consumerrights.wiki/w/Common_Questions,_Arguments,_%26_Responses_when_discussing_Flock_Surveillance"
|
href="https://consumerrights.wiki/w/Common_Questions,_Arguments,_%26_Responses_when_discussing_Flock_Surveillance"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<div class="d-flex align-center justify-space-between">
|
<div class="d-flex align-center justify-space-between">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<v-icon color="primary" class="mr-3 align-self-center">mdi-comment-alert</v-icon>
|
<v-icon color="primary" class="mr-3 align-self-center">mdi-comment-alert</v-icon>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="text-h6 font-weight-bold mb-1">Talking Points</h4>
|
<h4 class="text-h6 font-weight-bold mb-1">Talking Points</h4>
|
||||||
<p class="text-body-2 mb-0">Common questions, arguments & responses for discussing surveillance</p>
|
<p class="text-body-2 mb-0">Common questions, arguments & responses for discussing surveillance</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<v-icon color="primary" class="align-self-center">mdi-open-in-new</v-icon>
|
||||||
</div>
|
</div>
|
||||||
<v-icon color="primary" class="align-self-center">mdi-open-in-new</v-icon>
|
</v-card>
|
||||||
</div>
|
</v-col>
|
||||||
</v-card>
|
</v-row>
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- Step 1: One-on-One Meetings -->
|
<!-- Step 1: One-on-One Meetings -->
|
||||||
<v-row class="mb-12">
|
<v-row class="mb-12">
|
||||||
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
||||||
<v-card class="pa-6" elevation="3" rounded="lg">
|
|
||||||
<div class="d-flex align-center mb-4">
|
|
||||||
<v-avatar size="48" color="primary" class="mr-4">
|
|
||||||
<span class="text-h5 font-weight-bold white--text">1</span>
|
|
||||||
</v-avatar>
|
|
||||||
<div>
|
|
||||||
<h3 class="text-h5 font-weight-bold mb-1">Meet Council Members Privately</h3>
|
|
||||||
<p class="text-body-2 text-medium-emphasis mb-0">Start with personal conversations - often the most effective approach</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="text-body-1 mb-4">
|
|
||||||
Personal conversations with council members are often the most effective way to influence their vote.
|
|
||||||
These private meetings allow for deeper discussion and help them understand the human impact of surveillance policies.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<h4 class="text-h6 mb-3 d-flex align-center">
|
|
||||||
<v-icon color="primary" class="mr-2">mdi-calendar-plus</v-icon>
|
|
||||||
How to Schedule
|
|
||||||
</h4>
|
|
||||||
<v-list density="compact">
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Contact their office via phone or email</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Suggest meeting at a local coffee shop or their office</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Request just 15-20 minutes of their time</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Mention you're a constituent concerned about ALPRs</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<h4 class="text-h6 mb-3 d-flex align-center">
|
|
||||||
<v-icon color="primary" class="mr-2">mdi-lightbulb-on</v-icon>
|
|
||||||
Meeting Tips
|
|
||||||
</h4>
|
|
||||||
<v-list density="compact">
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Bring a brief printed summary of key points</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Share personal concerns about privacy and community impact</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Ask about their position and listen to their concerns</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Respond to their concerns with your ideas</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-divider class="my-4"></v-divider>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4 class="text-h6 font-weight-bold mb-2">Sample Email Template</h4>
|
|
||||||
<v-card class="pa-4" variant="tonal">
|
|
||||||
<p class="mb-0">
|
|
||||||
Hello [Council Member], I'm a [city] resident concerned about the upcoming
|
|
||||||
surveillance technology contract. Would you have 15 minutes to discuss this over coffee? I'd love to share some community
|
|
||||||
concerns and hear your thoughts.
|
|
||||||
</p>
|
|
||||||
</v-card>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- Step 2: Public Speaking -->
|
|
||||||
<v-row class="mb-12">
|
|
||||||
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
|
||||||
<v-card class="pa-6" elevation="3" rounded="lg">
|
|
||||||
<div class="d-flex align-center mb-4">
|
|
||||||
<v-avatar size="48" color="primary" class="mr-4">
|
|
||||||
<span class="text-h5 font-weight-bold white--text">2</span>
|
|
||||||
</v-avatar>
|
|
||||||
<div>
|
|
||||||
<h3 class="text-h5 font-weight-bold mb-1">Speak at Council Meetings</h3>
|
|
||||||
<p class="text-body-2 text-medium-emphasis mb-0">Create an official record and show public opposition</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="text-body-1 mb-4">
|
|
||||||
Public comment at council meetings creates an official record of community opposition and demonstrates
|
|
||||||
to council members that constituents are paying attention to their votes on surveillance issues.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<h4 class="text-h6 mb-3 d-flex align-center">
|
|
||||||
<v-icon color="primary" class="mr-2">mdi-clock</v-icon>
|
|
||||||
Before the Meeting
|
|
||||||
</h4>
|
|
||||||
<v-list density="compact">
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Check meeting schedule and agenda online</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Sign up for public comment (often required)</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Prepare 2-3 minute statement (practice timing)</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Bring a copy of your statement</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" md="6">
|
|
||||||
<h4 class="text-h6 mb-3 d-flex align-center">
|
|
||||||
<v-icon color="primary" class="mr-2">mdi-presentation</v-icon>
|
|
||||||
During the Meeting
|
|
||||||
</h4>
|
|
||||||
<v-list density="compact">
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Arrive on time or early</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>State your name and connection to the city</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Speak clearly and maintain eye contact</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item prepend-icon="mdi-check">
|
|
||||||
<v-list-item-title>Stay respectful and thank council for their time</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-divider class="my-4"></v-divider>
|
|
||||||
|
|
||||||
<v-alert type="info" variant="tonal" class="mb-4">
|
|
||||||
<strong>Pro Tip:</strong> Focus on how ALPRs affect YOUR community specifically. Personal stories and local examples are more compelling than abstract arguments.
|
|
||||||
</v-alert>
|
|
||||||
|
|
||||||
<!-- Example Videos -->
|
|
||||||
<div class="mt-6">
|
|
||||||
<h4 class="text-h6 font-weight-bold mb-4 d-flex align-center">
|
|
||||||
<v-icon color="primary" class="mr-2">mdi-video</v-icon>
|
|
||||||
Example Speeches
|
|
||||||
</h4>
|
|
||||||
<v-row>
|
|
||||||
<v-col v-for="video in videos" :key="video.url" cols="12" sm="6">
|
|
||||||
<v-card
|
|
||||||
class="video-card-compact pa-4 d-flex align-center"
|
|
||||||
elevation="2"
|
|
||||||
rounded="lg"
|
|
||||||
hover
|
|
||||||
@click="openVideo(video.url)"
|
|
||||||
>
|
|
||||||
<v-avatar size="48" color="primary" class="mr-3 video-play-button-compact">
|
|
||||||
<v-icon size="24" color="white">mdi-play</v-icon>
|
|
||||||
</v-avatar>
|
|
||||||
|
|
||||||
<div class="flex-grow-1">
|
|
||||||
<h4 class="text-body-1 font-weight-bold mb-0">{{ video.location }}</h4>
|
|
||||||
<p class="text-caption text-medium-emphasis mb-0">City Council Meeting</p>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- Success Stories -->
|
|
||||||
<v-row class="mb-12">
|
|
||||||
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
|
||||||
<div ref="tableRef" class="intersection-target">
|
|
||||||
<v-card class="pa-6" elevation="3" rounded="lg">
|
<v-card class="pa-6" elevation="3" rounded="lg">
|
||||||
<div class="d-flex align-center mb-4">
|
<div class="d-flex align-center mb-4">
|
||||||
<v-avatar size="48" color="primary" class="mr-4">
|
<v-avatar size="48" color="primary" class="mr-4">
|
||||||
<v-icon size="24" color="white">mdi-trophy</v-icon>
|
<span class="text-h5 font-weight-bold white--text">1</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-h5 font-weight-bold mb-1">Recent Victories</h3>
|
<h3 class="text-h5 font-weight-bold mb-1">Meet Council Members Privately</h3>
|
||||||
<p class="text-body-2 text-medium-emphasis mb-0">Communities across the country are winning</p>
|
<p class="text-body-2 text-medium-emphasis mb-0">Start with personal conversations - often the most effective approach</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-data-table
|
|
||||||
:headers="headers"
|
|
||||||
:items="citiesRejectingFlock"
|
|
||||||
:loading="loading"
|
|
||||||
:items-per-page="10"
|
|
||||||
:items-per-page-options="[10, 25, 50]"
|
|
||||||
class="elevation-0"
|
|
||||||
density="comfortable"
|
|
||||||
hover
|
|
||||||
show-expand
|
|
||||||
item-value="cityState"
|
|
||||||
>
|
|
||||||
<template v-slot:header.cityState="{ column }">
|
|
||||||
<div class="d-flex align-center text-medium-emphasis">
|
|
||||||
<v-icon icon="mdi-map-marker" size="18" class="mr-2" />
|
|
||||||
<span class="text-caption font-weight-medium">{{ column.title }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:header.MonthYear="{ column }">
|
<p class="text-body-1 mb-4">
|
||||||
<div class="d-flex align-center text-medium-emphasis">
|
Personal conversations with council members are often the most effective way to influence their vote.
|
||||||
<v-icon icon="mdi-calendar-month" size="18" class="mr-2" />
|
These private meetings allow for deeper discussion and help them understand the human impact of surveillance policies.
|
||||||
<span class="text-caption font-weight-medium">{{ column.title }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:header.Outcome="{ column }">
|
|
||||||
<div class="d-flex align-center text-medium-emphasis">
|
|
||||||
<v-icon icon="mdi-trophy-outline" size="18" class="mr-2" />
|
|
||||||
<span class="text-caption font-weight-medium">{{ column.title }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.data-table-expand="{ internalItem, isExpanded, toggleExpand }">
|
|
||||||
<v-btn
|
|
||||||
:append-icon="isExpanded(internalItem) ? 'mdi-chevron-up' : 'mdi-chevron-down'"
|
|
||||||
:text="isExpanded(internalItem) ? 'Collapse' : 'More info'"
|
|
||||||
class="text-none"
|
|
||||||
color="medium-emphasis"
|
|
||||||
size="small"
|
|
||||||
variant="text"
|
|
||||||
border
|
|
||||||
slim
|
|
||||||
@click="toggleExpand(internalItem)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:expanded-row="{ columns, item }">
|
|
||||||
<tr>
|
|
||||||
<td :colspan="columns.length" class="pa-4">
|
|
||||||
<div v-html="item.description" class="text-body-1" style="line-height: 1.6;"></div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.cityState="{ item }">
|
|
||||||
<span class="font-weight-bold">{{ item.cityState }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.MonthYear="{ item }">
|
|
||||||
{{ item.MonthYear }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.Outcome="{ item }">
|
|
||||||
<v-icon icon="mdi-check-bold" size="18" class="mr-2" />
|
|
||||||
<span class="font-weight-bold">{{ item.Outcome }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:loading>
|
|
||||||
<v-skeleton-loader type="table-row@10"></v-skeleton-loader>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:no-data>
|
|
||||||
<div class="text-center py-8">
|
|
||||||
<v-icon size="48" color="grey-lighten-1" class="mb-4">mdi-database-off</v-icon>
|
|
||||||
<div class="text-h6 text-medium-emphasis">No victories found</div>
|
|
||||||
<div class="text-body-2 text-medium-emphasis mt-2">
|
|
||||||
Check your connection and try again
|
|
||||||
</div>
|
|
||||||
<v-btn
|
|
||||||
@click="fetchRecentWins"
|
|
||||||
color="primary"
|
|
||||||
variant="outlined"
|
|
||||||
class="mt-4"
|
|
||||||
prepend-icon="mdi-refresh"
|
|
||||||
>
|
|
||||||
Retry
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</v-data-table>
|
|
||||||
|
|
||||||
<div v-if="lastUpdated" class="mt-4 text-caption text-medium-emphasis text-center">
|
|
||||||
Last updated: {{ formatDate(lastUpdated) }}
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- Community Support Section -->
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
|
||||||
<v-card class="pa-6" elevation="3" rounded="lg" color="primary" variant="tonal">
|
|
||||||
<div class="text-center">
|
|
||||||
<v-icon size="64" color="primary" class="mb-4">mdi-comment-question</v-icon>
|
|
||||||
<h3 class="text-h4 font-weight-bold mb-4">Need Help or Have Questions?</h3>
|
|
||||||
<p class="text-h6 mb-6 serif">
|
|
||||||
Join our supportive community of activists and experienced speakers who are ready to help you succeed.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<v-btn
|
|
||||||
href="https://discord.gg/aV7v4R3sKT"
|
|
||||||
target="_blank"
|
|
||||||
size="large"
|
|
||||||
color="primary"
|
|
||||||
class="mr-4 mb-4"
|
|
||||||
prepend-icon="mdi-discord"
|
|
||||||
>
|
|
||||||
Join #campaigning Channel
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<Footer/>
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<h4 class="text-h6 mb-3 d-flex align-center">
|
||||||
|
<v-icon color="primary" class="mr-2">mdi-calendar-plus</v-icon>
|
||||||
|
How to Schedule
|
||||||
|
</h4>
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Contact their office via phone or email</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Suggest meeting at a local coffee shop or their office</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Request just 15-20 minutes of their time</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Mention you're a constituent concerned about ALPRs</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<h4 class="text-h6 mb-3 d-flex align-center">
|
||||||
|
<v-icon color="primary" class="mr-2">mdi-lightbulb-on</v-icon>
|
||||||
|
Meeting Tips
|
||||||
|
</h4>
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Bring a brief printed summary of key points</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Share personal concerns about privacy and community impact</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Ask about their position and listen to their concerns</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Respond to their concerns with your ideas</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-divider class="my-4"></v-divider>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 class="text-h6 font-weight-bold mb-2">Sample Email Template</h4>
|
||||||
|
<v-card class="pa-4" variant="tonal">
|
||||||
|
<p class="mb-0">
|
||||||
|
Hello [Council Member], I'm a [city] resident concerned about the upcoming
|
||||||
|
surveillance technology contract. Would you have 15 minutes to discuss this over coffee? I'd love to share some community
|
||||||
|
concerns and hear your thoughts.
|
||||||
|
</p>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<!-- Step 2: Public Speaking -->
|
||||||
|
<v-row class="mb-12">
|
||||||
|
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
||||||
|
<v-card class="pa-6" elevation="3" rounded="lg">
|
||||||
|
<div class="d-flex align-center mb-4">
|
||||||
|
<v-avatar size="48" color="primary" class="mr-4">
|
||||||
|
<span class="text-h5 font-weight-bold white--text">2</span>
|
||||||
|
</v-avatar>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-h5 font-weight-bold mb-1">Speak at Council Meetings</h3>
|
||||||
|
<p class="text-body-2 text-medium-emphasis mb-0">Create an official record and show public opposition</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-body-1 mb-4">
|
||||||
|
Public comment at council meetings creates an official record of community opposition and demonstrates
|
||||||
|
to council members that constituents are paying attention to their votes on surveillance issues.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<h4 class="text-h6 mb-3 d-flex align-center">
|
||||||
|
<v-icon color="primary" class="mr-2">mdi-clock</v-icon>
|
||||||
|
Before the Meeting
|
||||||
|
</h4>
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Check meeting schedule and agenda online</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Sign up for public comment (often required)</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Prepare 2-3 minute statement (practice timing)</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Bring a copy of your statement</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<h4 class="text-h6 mb-3 d-flex align-center">
|
||||||
|
<v-icon color="primary" class="mr-2">mdi-presentation</v-icon>
|
||||||
|
During the Meeting
|
||||||
|
</h4>
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Arrive on time or early</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>State your name and connection to the city</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Speak clearly and maintain eye contact</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item prepend-icon="mdi-check">
|
||||||
|
<v-list-item-title>Stay respectful and thank council for their time</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-divider class="my-4"></v-divider>
|
||||||
|
|
||||||
|
<v-alert type="info" variant="tonal" class="mb-4">
|
||||||
|
<strong>Pro Tip:</strong> Focus on how ALPRs affect YOUR community specifically. Personal stories and local examples are more compelling than abstract arguments.
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
|
<!-- Example Videos -->
|
||||||
|
<div class="mt-6">
|
||||||
|
<h4 class="text-h6 font-weight-bold mb-4 d-flex align-center">
|
||||||
|
<v-icon color="primary" class="mr-2">mdi-video</v-icon>
|
||||||
|
Example Speeches
|
||||||
|
</h4>
|
||||||
|
<v-row>
|
||||||
|
<v-col v-for="video in videos" :key="video.url" cols="12" sm="6">
|
||||||
|
<v-card
|
||||||
|
class="video-card-compact pa-4 d-flex align-center"
|
||||||
|
elevation="2"
|
||||||
|
rounded="lg"
|
||||||
|
hover
|
||||||
|
@click="openVideo(video.url)"
|
||||||
|
>
|
||||||
|
<v-avatar size="48" color="primary" class="mr-3 video-play-button-compact">
|
||||||
|
<v-icon size="24" color="white">mdi-play</v-icon>
|
||||||
|
</v-avatar>
|
||||||
|
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<h4 class="text-body-1 font-weight-bold mb-0">{{ video.location }}</h4>
|
||||||
|
<p class="text-caption text-medium-emphasis mb-0">City Council Meeting</p>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<!-- Success Stories -->
|
||||||
|
<v-row class="mb-12">
|
||||||
|
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
||||||
|
<div ref="tableRef" class="intersection-target">
|
||||||
|
<v-card class="pa-6" elevation="3" rounded="lg">
|
||||||
|
<div class="d-flex align-center mb-4">
|
||||||
|
<v-avatar size="48" color="primary" class="mr-4">
|
||||||
|
<v-icon size="24" color="white">mdi-trophy</v-icon>
|
||||||
|
</v-avatar>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-h5 font-weight-bold mb-1">Recent Victories</h3>
|
||||||
|
<p class="text-body-2 text-medium-emphasis mb-0">Communities across the country are winning</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-data-table
|
||||||
|
:headers="headers"
|
||||||
|
:items="citiesRejectingFlock"
|
||||||
|
:loading="loading"
|
||||||
|
:items-per-page="10"
|
||||||
|
:items-per-page-options="[10, 25, 50]"
|
||||||
|
class="elevation-0"
|
||||||
|
density="comfortable"
|
||||||
|
hover
|
||||||
|
show-expand
|
||||||
|
item-value="cityState"
|
||||||
|
>
|
||||||
|
<template v-slot:header.cityState="{ column }">
|
||||||
|
<div class="d-flex align-center text-medium-emphasis">
|
||||||
|
<v-icon icon="mdi-map-marker" size="18" class="mr-2" />
|
||||||
|
<span class="text-caption font-weight-medium">{{ column.title }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header.MonthYear="{ column }">
|
||||||
|
<div class="d-flex align-center text-medium-emphasis">
|
||||||
|
<v-icon icon="mdi-calendar-month" size="18" class="mr-2" />
|
||||||
|
<span class="text-caption font-weight-medium">{{ column.title }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header.Outcome="{ column }">
|
||||||
|
<div class="d-flex align-center text-medium-emphasis">
|
||||||
|
<v-icon icon="mdi-trophy-outline" size="18" class="mr-2" />
|
||||||
|
<span class="text-caption font-weight-medium">{{ column.title }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.data-table-expand="{ internalItem, isExpanded, toggleExpand }">
|
||||||
|
<v-btn
|
||||||
|
:append-icon="isExpanded(internalItem) ? 'mdi-chevron-up' : 'mdi-chevron-down'"
|
||||||
|
:text="isExpanded(internalItem) ? 'Collapse' : 'More info'"
|
||||||
|
class="text-none"
|
||||||
|
color="medium-emphasis"
|
||||||
|
size="small"
|
||||||
|
variant="text"
|
||||||
|
border
|
||||||
|
slim
|
||||||
|
@click="toggleExpand(internalItem)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:expanded-row="{ columns, item }">
|
||||||
|
<tr>
|
||||||
|
<td :colspan="columns.length" class="pa-4">
|
||||||
|
<div v-html="item.description" class="text-body-1" style="line-height: 1.6;"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.cityState="{ item }">
|
||||||
|
<span class="font-weight-bold">{{ item.cityState }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.MonthYear="{ item }">
|
||||||
|
{{ item.MonthYear }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.Outcome="{ item }">
|
||||||
|
<v-icon icon="mdi-check-bold" size="18" class="mr-2" />
|
||||||
|
<span class="font-weight-bold">{{ item.Outcome }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:loading>
|
||||||
|
<v-skeleton-loader type="table-row@10"></v-skeleton-loader>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:no-data>
|
||||||
|
<div class="text-center py-8">
|
||||||
|
<v-icon size="48" color="grey-lighten-1" class="mb-4">mdi-database-off</v-icon>
|
||||||
|
<div class="text-h6 text-medium-emphasis">No victories found</div>
|
||||||
|
<div class="text-body-2 text-medium-emphasis mt-2">
|
||||||
|
Check your connection and try again
|
||||||
|
</div>
|
||||||
|
<v-btn
|
||||||
|
@click="fetchRecentWins"
|
||||||
|
color="primary"
|
||||||
|
variant="outlined"
|
||||||
|
class="mt-4"
|
||||||
|
prepend-icon="mdi-refresh"
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
|
||||||
|
<div v-if="lastUpdated" class="mt-4 text-caption text-medium-emphasis text-center">
|
||||||
|
Last updated: {{ formatDate(lastUpdated) }}
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<!-- Community Support Section -->
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="10" lg="8" class="mx-auto">
|
||||||
|
<v-card class="pa-6" elevation="3" rounded="lg" color="primary" variant="tonal">
|
||||||
|
<div class="text-center">
|
||||||
|
<v-icon size="64" color="primary" class="mb-4">mdi-comment-question</v-icon>
|
||||||
|
<h3 class="text-h4 font-weight-bold mb-4">Need Help or Have Questions?</h3>
|
||||||
|
<p class="text-h6 mb-6 serif">
|
||||||
|
Join our supportive community of activists and experienced speakers who are ready to help you succeed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
href="https://discord.gg/aV7v4R3sKT"
|
||||||
|
target="_blank"
|
||||||
|
size="large"
|
||||||
|
color="primary"
|
||||||
|
class="mr-4 mb-4"
|
||||||
|
prepend-icon="mdi-discord"
|
||||||
|
>
|
||||||
|
Join #campaigning Channel
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
|
||||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
|
|
||||||
const sortMonthYearByDateDesc = (a: string, b: string) => {
|
const sortMonthYearByDateDesc = (a: string, b: string) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<DefaultLayout no-bottom-margin>
|
||||||
<div class="app-page">
|
<div class="app-page">
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<section class="hero-section">
|
<section class="hero-section">
|
||||||
@@ -218,11 +219,11 @@
|
|||||||
</v-container>
|
</v-container>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
interface Feature {
|
interface Feature {
|
||||||
|
|||||||
@@ -1,111 +1,113 @@
|
|||||||
<template>
|
<template>
|
||||||
<Hero
|
<DefaultLayout>
|
||||||
title="App User Guide"
|
<template #header>
|
||||||
description="Learn how to use the Deflock app."
|
<Hero
|
||||||
gradient="linear-gradient(135deg, rgb(var(--v-theme-primary)) 0%, rgb(var(--v-theme-secondary)) 100%)"
|
title="App User Guide"
|
||||||
/>
|
description="Learn how to use the Deflock app."
|
||||||
|
gradient="linear-gradient(135deg, rgb(var(--v-theme-primary)) 0%, rgb(var(--v-theme-secondary)) 100%)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container class="documentation-content" fluid>
|
<v-container class="documentation-content" fluid>
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<v-row>
|
<v-row>
|
||||||
<!-- Desktop TOC Sidebar Column -->
|
<!-- Desktop TOC Sidebar Column -->
|
||||||
<v-col
|
<v-col
|
||||||
v-if="!loading && !error && tocItems.length > 0"
|
v-if="!loading && !error && tocItems.length > 0"
|
||||||
cols="12"
|
cols="12"
|
||||||
md="3"
|
md="3"
|
||||||
lg="3"
|
lg="3"
|
||||||
xl="2"
|
xl="2"
|
||||||
class="d-none d-md-block toc-column"
|
class="d-none d-md-block toc-column"
|
||||||
>
|
>
|
||||||
<v-card class="toc-sidebar" elevation="1" sticky>
|
<v-card class="toc-sidebar" elevation="1" sticky>
|
||||||
<v-card-title class="text-h6 py-3">
|
<v-card-title class="text-h6 py-3">
|
||||||
<v-icon start>mdi-format-list-bulleted</v-icon>
|
<v-icon start>mdi-format-list-bulleted</v-icon>
|
||||||
Table of Contents
|
Table of Contents
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<v-card-text class="py-2 toc-scrollable">
|
<v-card-text class="py-2 toc-scrollable">
|
||||||
<nav class="toc-desktop">
|
<nav class="toc-desktop">
|
||||||
<template v-for="item in tocItems" :key="item.id">
|
<template v-for="item in tocItems" :key="item.id">
|
||||||
<div class="toc-item" :class="`toc-level-${item.level}`">
|
<div class="toc-item" :class="`toc-level-${item.level}`">
|
||||||
<button
|
<button
|
||||||
@click="scrollToSection(item.id)"
|
@click="scrollToSection(item.id)"
|
||||||
class="toc-link"
|
class="toc-link"
|
||||||
:class="{ 'toc-active': activeSection === item.id }"
|
:class="{ 'toc-active': activeSection === item.id }"
|
||||||
>
|
>
|
||||||
<span class="toc-text">{{ item.text }}</span>
|
<span class="toc-text">{{ item.text }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Render children recursively -->
|
<!-- Render children recursively -->
|
||||||
<template v-if="item.children && item.children.length > 0">
|
<template v-if="item.children && item.children.length > 0">
|
||||||
<template v-for="child in item.children" :key="child.id">
|
<template v-for="child in item.children" :key="child.id">
|
||||||
<div class="toc-item" :class="`toc-level-${child.level}`">
|
<div class="toc-item" :class="`toc-level-${child.level}`">
|
||||||
<button
|
|
||||||
@click="scrollToSection(child.id)"
|
|
||||||
class="toc-link"
|
|
||||||
:class="{ 'toc-active': activeSection === child.id }"
|
|
||||||
>
|
|
||||||
<span class="toc-text">{{ child.text }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Third level -->
|
|
||||||
<template v-if="child.children && child.children.length > 0">
|
|
||||||
<div
|
|
||||||
v-for="grandchild in child.children"
|
|
||||||
:key="grandchild.id"
|
|
||||||
class="toc-item"
|
|
||||||
:class="`toc-level-${grandchild.level}`"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
@click="scrollToSection(grandchild.id)"
|
@click="scrollToSection(child.id)"
|
||||||
class="toc-link"
|
class="toc-link"
|
||||||
:class="{ 'toc-active': activeSection === grandchild.id }"
|
:class="{ 'toc-active': activeSection === child.id }"
|
||||||
>
|
>
|
||||||
<span class="toc-text">{{ grandchild.text }}</span>
|
<span class="toc-text">{{ child.text }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Third level -->
|
||||||
|
<template v-if="child.children && child.children.length > 0">
|
||||||
|
<div
|
||||||
|
v-for="grandchild in child.children"
|
||||||
|
:key="grandchild.id"
|
||||||
|
class="toc-item"
|
||||||
|
:class="`toc-level-${grandchild.level}`"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
@click="scrollToSection(grandchild.id)"
|
||||||
|
class="toc-link"
|
||||||
|
:class="{ 'toc-active': activeSection === grandchild.id }"
|
||||||
|
>
|
||||||
|
<span class="toc-text">{{ grandchild.text }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</nav>
|
||||||
</nav>
|
</v-card-text>
|
||||||
</v-card-text>
|
</v-card>
|
||||||
</v-card>
|
</v-col>
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<!-- Main Content Column -->
|
<!-- Main Content Column -->
|
||||||
<v-col
|
<v-col
|
||||||
:cols="12"
|
:cols="12"
|
||||||
:md="(!loading && !error && tocItems.length > 0) ? 9 : 12"
|
:md="(!loading && !error && tocItems.length > 0) ? 9 : 12"
|
||||||
:lg="(!loading && !error && tocItems.length > 0) ? 9 : 12"
|
:lg="(!loading && !error && tocItems.length > 0) ? 9 : 12"
|
||||||
:xl="(!loading && !error && tocItems.length > 0) ? 10 : 12"
|
:xl="(!loading && !error && tocItems.length > 0) ? 10 : 12"
|
||||||
class="content-column"
|
class="content-column"
|
||||||
>
|
>
|
||||||
<div v-if="loading" class="text-center my-8">
|
<div v-if="loading" class="text-center my-8">
|
||||||
<v-progress-circular indeterminate color="primary" size="64" />
|
<v-progress-circular indeterminate color="primary" size="64" />
|
||||||
<p class="mt-4">Loading user guide...</p>
|
<p class="mt-4">Loading user guide...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="error" class="text-center my-8">
|
<div v-else-if="error" class="text-center my-8">
|
||||||
<v-alert type="error" variant="tonal" class="mb-4">
|
<v-alert type="error" variant="tonal" class="mb-4">
|
||||||
Failed to load user guide: {{ error }}
|
Failed to load user guide: {{ error }}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article v-else-if="content" class="docs-content" v-html="content" />
|
<article v-else-if="content" class="docs-content" v-html="content" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</div>
|
</div>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, nextTick, watch } from 'vue';
|
import { ref, onMounted, nextTick, watch } from 'vue';
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
|
||||||
|
|
||||||
interface CMSResponse {
|
interface CMSResponse {
|
||||||
data: {
|
data: {
|
||||||
@@ -271,8 +273,6 @@ watch(content, () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import url('@/assets/typography.css');
|
|
||||||
|
|
||||||
/* Container styles */
|
/* Container styles */
|
||||||
.documentation-content {
|
.documentation-content {
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container fluid class="sponsor-page">
|
<DefaultLayout>
|
||||||
<!-- Hero Section -->
|
<template #header>
|
||||||
<v-row justify="center" class="hero-section-sponsor text-center mb-4">
|
<Hero
|
||||||
<v-col cols="12" md="8">
|
title="Support DeFlock"
|
||||||
<h1 class="mb-4">Join Us in Protecting Privacy</h1>
|
subtitle="Your contributions help us fight for privacy and raise awareness about ALPR technology."
|
||||||
<p class="mb-4">
|
image-url="/donate.webp"
|
||||||
DeFlock empowers individuals to understand and combat the rise of Automatic License Plate Readers (ALPRs). Your support helps us spread awareness, maintain infrastructure, and advocate for privacy rights.
|
background-position="0 25%"
|
||||||
</p>
|
button-text="Donate Now"
|
||||||
<v-btn href="https://github.com/sponsors/frillweeman" target="_blank" color="rgb(18, 151, 195)" class="mt-4">Donate Now</v-btn>
|
button-href="https://github.com/sponsors/frillweeman"
|
||||||
</v-col>
|
:opacity="0.25"
|
||||||
</v-row>
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-container fluid>
|
||||||
<!-- GitHub Sponsors Section -->
|
<!-- GitHub Sponsors Section -->
|
||||||
<v-row justify="center" class="sponsors-section text-center">
|
<v-row justify="center" class="sponsors-section text-center">
|
||||||
<v-col cols="12" md="10">
|
<v-col cols="12" md="10">
|
||||||
@@ -41,23 +43,23 @@
|
|||||||
<h2 class="mb-2">Special Thanks</h2>
|
<h2 class="mb-2">Special Thanks</h2>
|
||||||
|
|
||||||
<v-card href="https://www.404media.co/" target="_blank" max-width="250" variant="flat" class="text-center py-2 mx-auto" color="transparent">
|
<v-card href="https://www.404media.co/" target="_blank" max-width="250" variant="flat" class="text-center py-2 mx-auto" color="transparent">
|
||||||
<v-img class="ma-4" src="/404media.svg" alt="404 Media Logo" contain />
|
<v-img class="ma-4" src="/404media.svg" alt="404 Media Logo" contain style="background: rgb(245,245,245)" />
|
||||||
<v-card-title class="mt-2 serif text-center font-weight-bold">404 Media</v-card-title>
|
<v-card-title class="mt-2 serif text-center font-weight-bold">404 Media</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<p>Special thanks to Jason Koebler at 404 Media for popularizing this project. Be sure to support them, as they are fellow privacy advocates.</p>
|
<p>Special thanks to Jason Koebler at 404 Media for popularizing this project.</p>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, type Ref } from "vue";
|
import { ref, onMounted, type Ref } from "vue";
|
||||||
import { getSponsors } from "@/services/apiService";
|
import { getSponsors } from "@/services/apiService";
|
||||||
import Footer from "@/components/layout/Footer.vue";
|
import DefaultLayout from "@/layouts/DefaultLayout.vue";
|
||||||
|
import Hero from "@/components/layout/Hero.vue";
|
||||||
|
|
||||||
interface Sponsor {
|
interface Sponsor {
|
||||||
login: string;
|
login: string;
|
||||||
@@ -82,29 +84,3 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.hero-section-sponsor {
|
|
||||||
background: url('/torches.webp') no-repeat center center;
|
|
||||||
background-size: cover;
|
|
||||||
color: white;
|
|
||||||
padding: 100px 0 !important;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section-sponsor::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section-sponsor > * {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,112 +1,90 @@
|
|||||||
<template>
|
<template>
|
||||||
<Hero
|
<DefaultLayout>
|
||||||
imageUrl="/documents.jpg"
|
<template #header>
|
||||||
title="How to Request Public Records"
|
<Hero
|
||||||
description="Learn how simple it is to request ALPR data from your city"
|
image-url="/documents.jpg"
|
||||||
button-text="Get Started"
|
title="How to Request Public Records"
|
||||||
button-href="#how-to"
|
description="Learn how simple it is to request ALPR data from your city"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container class="mb-12">
|
<v-container class="narrow-text">
|
||||||
<h2>Why File a Public Records Request?</h2>
|
<h2>Why File a Public Records Request?</h2>
|
||||||
<p>
|
<p>
|
||||||
ALPRs are being installed across the country with little transparency. Public records requests allow us to track where these cameras are, ensuring that all installations are documented.
|
ALPRs are being installed across the country with little transparency. Public records requests allow us to track where these cameras are, ensuring that all installations are documented.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Your help is crucial. Filing a public records request is easy, and your contribution will help us build a complete map of these surveillance networks. We'll even link you to several hundred <a target="_blank" href="https://www.muckrock.com/search/?q=Flock">examples to follow</a>.<sup>[1]</sup>
|
Your help is crucial. Filing a public records request is easy, and your contribution will help us build a complete map of these surveillance networks. We'll even link you to several hundred <a target="_blank" href="https://www.muckrock.com/search/?q=Flock">examples to follow</a>.<sup>[1]</sup>
|
||||||
</p>
|
</p>
|
||||||
<footer class="serif text-grey-darken-2 mt-4">
|
<footer class="serif text-grey-darken-2 mt-4">
|
||||||
<small><sup>[1]</sup> While these examples focus on Flock Safety, the process applies to all ALPR vendors. Flock is highlighted here because it is the most widely reported ALPR vendor in the US on OpenStreetMap at the time this was written.</small>
|
<small><sup>[1]</sup> While these examples focus on Flock Safety, the process applies to all ALPR vendors. Flock is highlighted here because it is the most widely reported ALPR vendor in the US on OpenStreetMap at the time this was written.</small>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<v-divider class="my-8" />
|
<v-divider class="my-8" />
|
||||||
|
|
||||||
<h2 id="how-to">How to File a Public Records Request</h2>
|
<h2>How to File a Public Records Request</h2>
|
||||||
<ol class="serif">
|
<ol class="serif">
|
||||||
<li class="mb-4">
|
<li class="mb-4">
|
||||||
Determine the correct agency that would have the responsive documents. For contracts and invoices, this is likely a <b>law enforcement agency</b> such as the police department or sheriff's office. For other records, such as installation permits, this might be the <b>city or county government</b>. If you're unsure, consider submitting requests to multiple agencies or researching which agency oversees the type of records you're seeking.
|
Determine the correct agency that would have the responsive documents. For contracts and invoices, this is likely a <b>law enforcement agency</b> such as the police department or sheriff's office. For other records, such as installation permits, this might be the <b>city or county government</b>. If you're unsure, consider submitting requests to multiple agencies or researching which agency oversees the type of records you're seeking.
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-4">
|
<li class="mb-4">
|
||||||
Locate their public records request portal. If they don't have one, find the relevant contact email.
|
Locate their public records request portal. If they don't have one, find the relevant contact email.
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-4">
|
<li class="mb-4">
|
||||||
<p>
|
<p>
|
||||||
Browse <a href="https://www.muckrock.com/search/?q=Flock" target="_blank">Muckrock</a> for examples of public records requests related to ALPRs. Use these as templates to draft your own request. Here are some common requests you can adapt:
|
Browse <a href="https://www.muckrock.com/search/?q=Flock" target="_blank">Muckrock</a> for examples of public records requests related to ALPRs. Use these as templates to draft your own request. Here are some common requests you can adapt:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Right-of-Way Installation Permits (typically include a map of locations)</li>
|
<li>Right-of-Way Installation Permits (typically include a map of locations)</li>
|
||||||
<li>Invoices for ALPRs (contain count of cameras)</li>
|
<li>Invoices for ALPRs (contain count of cameras)</li>
|
||||||
<li>Contracts with ALPR vendors (show proof of usage)</li>
|
<li>Contracts with ALPR vendors (show proof of usage)</li>
|
||||||
<li>Emails discussing ALPR installations</li>
|
<li>Emails discussing ALPR installations</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p><b>If you need help tailoring this to your situation, appealing rejections, etc., ChatGPT is a great resource.</b></p>
|
<p><b>If you need help tailoring this to your situation, appealing rejections, etc., ChatGPT is a great resource.</b></p>
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-4">
|
<li class="mb-4">
|
||||||
If you receive responsive documents that include location info, <router-link to="/report">add them to the site</router-link>.
|
If you receive responsive documents that include location info, <router-link to="/report">add them to the site</router-link>.
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<v-alert class="mt-8" type="info" variant="tonal">
|
<v-alert class="mt-8" type="info" variant="tonal">
|
||||||
<b>Tip:</b> If you run into any issues while submitting the public records request, such as not being able to locate the records portal or find a contact email, you can use <a href="https://muckrock.com" target="_blank">Muckrock</a>, a nonprofit public records platform that will submit it to the agency on your behalf for a small fee.
|
<b>Tip:</b> If you run into any issues while submitting the public records request, such as not being able to locate the records portal or find a contact email, you can use <a href="https://muckrock.com" target="_blank">Muckrock</a>, a nonprofit public records platform that will submit it to the agency on your behalf for a small fee.
|
||||||
</v-alert>
|
</v-alert>
|
||||||
|
|
||||||
<v-divider class="my-8" />
|
<v-divider class="my-8" />
|
||||||
|
|
||||||
<h2>Public Records Basics</h2>
|
<h2>Public Records Basics</h2>
|
||||||
|
|
||||||
<h3>What is a public records request?</h3>
|
<h3>What is a public records request?</h3>
|
||||||
<p>
|
<p>
|
||||||
A public records request is a formal way to ask government agencies for public records. Laws vary by state, but most allow residents to request documents such as contracts, emails, and invoices.
|
A public records request is a formal way to ask government agencies for public records. Laws vary by state, but most allow residents to request documents such as contracts, emails, and invoices.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Who can file a public records request?</h3>
|
<h3>Who can file a public records request?</h3>
|
||||||
<p>
|
<p>
|
||||||
Most states allow anyone to file a public records request, while some restrict it to state residents.
|
Most states allow anyone to file a public records request, while some restrict it to state residents.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>What can be requested?</h3>
|
<h3>What can be requested?</h3>
|
||||||
<p>
|
<p>
|
||||||
Invoices, contracts, emails, and other documents related to government spending and agreements with companies, including these surveillance companies.
|
Invoices, contracts, emails, and other documents related to government spending and agreements with companies, including these surveillance companies.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>What if my request is rejected?</h3>
|
<h3>What if my request is rejected?</h3>
|
||||||
<p>
|
<p>
|
||||||
Agencies may reject requests for various reasons, but you can often appeal. <a href="https://discord.gg/aV7v4R3sKT" target="_blank">Let us know</a> if you encounter issues, and we can help.
|
Agencies may reject requests for various reasons, but you can often appeal. <a href="https://discord.gg/aV7v4R3sKT" target="_blank">Let us know</a> if you encounter issues, and we can help.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Can I remain anonymous?</h3>
|
|
||||||
<p>
|
|
||||||
Typically, yes. Many states allow you to submit public records requests anonymously or through an alias. If anonymity is important to you, consider using a disposable email address and avoiding personally identifying information in your request. If you're requesting records from a state that requires residency, you may need to provide an in-state address.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
|
|
||||||
|
<h3>Can I remain anonymous?</h3>
|
||||||
|
<p>
|
||||||
|
Typically, yes. Many states allow you to submit public records requests anonymously or through an alias. If anonymity is important to you, consider using a disposable email address and avoiding personally identifying information in your request. If you're requesting records from a state that requires residency, you may need to provide an in-state address.
|
||||||
|
</p>
|
||||||
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSeoMeta } from '@unhead/vue';
|
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
|
|
||||||
useSeoMeta({
|
|
||||||
title: 'How to Request Public Records | DeFlock',
|
|
||||||
description: 'Learn how simple it is to request ALPR data from your city.',
|
|
||||||
|
|
||||||
ogTitle: 'How to Request ALPR Data',
|
|
||||||
ogDescription: 'Learn how simple it is to request ALPR data from your city.',
|
|
||||||
ogImage: 'https://deflock.me/documents.jpg',
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
@import url('@/assets/typography.css');
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 1em 0;
|
|
||||||
padding-left: 1em;
|
|
||||||
border-left: 4px solid #ccc;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- Hero Section -->
|
<DefaultLayout>
|
||||||
<Hero
|
<template #header>
|
||||||
title="Spot an ALPR"
|
<Hero
|
||||||
description="Visual guide to identifying license plate readers"
|
title="Spot an ALPR"
|
||||||
gradient="linear-gradient(135deg, rgb(var(--v-theme-primary)) 0%, rgb(var(--v-theme-secondary)) 100%)"
|
description="Visual guide to identifying license plate readers"
|
||||||
/>
|
gradient="linear-gradient(135deg, rgb(var(--v-theme-primary)) 0%, rgb(var(--v-theme-secondary)) 100%)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container fluid>
|
<v-container fluid>
|
||||||
<!-- Flock Safety - Featured Section -->
|
<!-- Flock Safety - Featured Section -->
|
||||||
@@ -151,13 +153,12 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
|
||||||
|
|
||||||
function openImageInNewTab(url: string) {
|
function openImageInNewTab(url: string) {
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
|
|||||||
@@ -1,29 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- Hero Section -->
|
<DefaultLayout no-bottom-margin>
|
||||||
<v-container fluid class="hero-section">
|
<template #header>
|
||||||
<v-row justify="center">
|
<v-container fluid class="hero-section">
|
||||||
<v-col cols="12" md="8" class="text-center">
|
<v-row justify="center">
|
||||||
<h1 class="display-1 px-8">You're Being Tracked</h1>
|
<v-col cols="12" md="8" class="text-center">
|
||||||
|
<h1 class="display-1 px-8">You're Being Tracked</h1>
|
||||||
<ALPRCounter class="my-6" />
|
|
||||||
|
<ALPRCounter class="my-6" />
|
||||||
<!-- Featured On Section -->
|
|
||||||
<v-container class="featured-on-section my-8">
|
<!-- Featured On Section -->
|
||||||
<h4 class="mb-4" style="opacity: 0.8">Featured On</h4>
|
<v-container class="featured-on-section my-8">
|
||||||
<v-row justify="center" align-items="center">
|
<h4 class="mb-4" style="opacity: 0.8">Featured On</h4>
|
||||||
<v-card v-for="site in featuredOn" flat target="_blank" :href="site.url" class="mx-4" :width="site.wide ? 200 : 100" height="50" style="background: rgba(0,0,0,0)">
|
<v-row justify="center" align-items="center">
|
||||||
<v-img contain :src="site.logo" :alt="site.name" class="featured-logo" style="display: flex; align-items: center; height: 100%;" />
|
<v-card v-for="site in featuredOn" flat target="_blank" :href="site.url" class="mx-4" :width="site.wide ? 200 : 100" height="50" style="background: rgba(0,0,0,0)">
|
||||||
</v-card>
|
<v-img contain :src="site.logo" :alt="site.name" class="featured-logo" style="display: flex; align-items: center; height: 100%;" />
|
||||||
</v-row>
|
</v-card>
|
||||||
</v-container>
|
</v-row>
|
||||||
|
</v-container>
|
||||||
<v-btn size="large" color="rgb(18, 151, 195)" large @click="goToMap({ withCurrentLocation: true })">
|
|
||||||
Explore the Map
|
<v-btn size="large" color="rgb(18, 151, 195)" large @click="goToMap({ withCurrentLocation: true })">
|
||||||
<v-icon end>mdi-map</v-icon>
|
Explore the Map
|
||||||
</v-btn>
|
<v-icon end>mdi-map</v-icon>
|
||||||
</v-col>
|
</v-btn>
|
||||||
</v-row>
|
</v-col>
|
||||||
</v-container>
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Dangers Section -->
|
<!-- Dangers Section -->
|
||||||
<v-container class="py-10 text-center info-section">
|
<v-container class="py-10 text-center info-section">
|
||||||
@@ -100,8 +102,7 @@
|
|||||||
<v-icon end>mdi-map</v-icon>
|
<v-icon end>mdi-map</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -167,7 +168,7 @@
|
|||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import ALPRCounter from '@/components/ALPRCounter.vue';
|
import ALPRCounter from '@/components/ALPRCounter.vue';
|
||||||
import { useGlobalStore } from '@/stores/global';
|
import { useGlobalStore } from '@/stores/global';
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { setCurrentLocation } = useGlobalStore();
|
const { setCurrentLocation } = useGlobalStore();
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
<template>
|
<template>
|
||||||
<Hero
|
<DefaultLayout>
|
||||||
imageUrl="/press.jpg"
|
<template #header>
|
||||||
title="Press"
|
<Hero
|
||||||
description="We're grateful for the media's interest in DeFlock. Below you'll find our press kit and contact information for interviews."
|
imageUrl="/press.jpg"
|
||||||
/>
|
title="Press"
|
||||||
|
description="We're grateful for the media's interest in DeFlock. Below you'll find our press kit and contact information for interviews."
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container class="mb-16">
|
<v-container class="narrow-text">
|
||||||
<h2>What is DeFlock?</h2>
|
<h2>What is DeFlock?</h2>
|
||||||
<p>
|
<p>
|
||||||
DeFlock is a community-driven project that maps surveillance devices across the world. We help communities hold their government and surveillance vendors accountable and understand where and how they're being watched.
|
DeFlock is a community-driven project that maps surveillance devices across the world. We help communities hold their government and surveillance vendors accountable and understand where and how they're being watched.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Founder Bio</h2>
|
<h2>Founder Bio</h2>
|
||||||
<p>
|
<p>
|
||||||
DeFlock was founded in October 2024 by Will Freeman, a software engineer and privacy advocate. On a road trip from Washington state to Alabama, he noticed hundreds of newly-installed cameras mounted on black poles with solar panels in nearly every town he passed through. Curious about their purpose, he discovered they were <a target="_blank" href="https://en.wikipedia.org/wiki/Flock_Safety">Flock cameras</a> used for surveillance. Upset about this trend to blatantly disregard the Fourth Amendment, he created a platform that maps these devices, empowering communities to understand and challenge surveillance practices.
|
DeFlock was founded in October 2024 by Will Freeman, a software engineer and privacy advocate. On a road trip from Washington state to Alabama, he noticed hundreds of newly-installed cameras mounted on black poles with solar panels in nearly every town he passed through. Curious about their purpose, he discovered they were <a target="_blank" href="https://en.wikipedia.org/wiki/Flock_Safety">Flock cameras</a> used for surveillance. Upset about this trend to blatantly disregard the Fourth Amendment, he created a platform that maps these devices, empowering communities to understand and challenge surveillance practices.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>Since its launch, DeFlock has received significant media coverage and community support, including code contributors and volunteer mappers, helping to raise awareness about the dangers of surveillance technology.</p>
|
<p>Since its launch, DeFlock has received significant media coverage and community support, including code contributors and volunteer mappers, helping to raise awareness about the dangers of surveillance technology.</p>
|
||||||
|
|
||||||
<h2>Media Kit</h2>
|
<h2>Media Kit</h2>
|
||||||
<p>
|
<p>
|
||||||
Download our very basic media kit <a target="_blank" href="https://cdn.deflock.me/deflock-media-kit.zip">here</a>.
|
Download our very basic media kit <a target="_blank" href="https://cdn.deflock.me/deflock-media-kit.zip">here</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Embedding our Map</h2>
|
<h2>Embedding our Map</h2>
|
||||||
<p>
|
<p>
|
||||||
You are welcome to embed our map on your website. Use the following HTML snippet:
|
You are welcome to embed our map on your website. Use the following HTML snippet:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<DFCode>
|
<DFCode>
|
||||||
<iframe src="https://deflock.me/map" width="100%" height="600" style="border: none;"></iframe>
|
<iframe src="https://deflock.me/map" width="100%" height="600" style="border: none;"></iframe>
|
||||||
</DFCode>
|
</DFCode>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If you would like to <b>localize the URL</b> to a specific region, please zoom to the area at <router-link to="/map">https://deflock.me/map</router-link> and copy the URL from your browser's address bar.
|
If you would like to <b>localize the URL</b> to a specific region, please zoom to the area at <router-link to="/map">https://deflock.me/map</router-link> and copy the URL from your browser's address bar.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
To <b>remove the header bar</b>, add the following query parameter to the URL: <code>?fullscreen=true</code>. For example:
|
To <b>remove the header bar</b>, add the following query parameter to the URL: <code>?fullscreen=true</code>. For example:
|
||||||
<DFCode>
|
<DFCode>
|
||||||
<iframe src="http://localhost:5173/map?fullscreen=true#map=14/40.014863/-105.266275" width="100%" height="600" style="border: none;"></iframe>
|
<iframe src="http://localhost:5173/map?fullscreen=true#map=14/40.014863/-105.266275" width="100%" height="600" style="border: none;"></iframe>
|
||||||
</DFCode>
|
</DFCode>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Contact Us</h2>
|
<h2>Contact Us</h2>
|
||||||
<p>
|
<p>
|
||||||
For press inquiries and interview requests, <router-link to="/contact" >please contact us here</router-link>.
|
For press inquiries and interview requests, <router-link to="/contact" >please contact us here</router-link>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
<Footer />
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import "@/assets/typography.css"
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import Footer from "@/components/layout/Footer.vue";
|
|
||||||
import DFCode from "@/components/DFCode.vue";
|
import DFCode from "@/components/DFCode.vue";
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="mb-16">
|
<DefaultLayout>
|
||||||
|
<v-container class="narrow-text">
|
||||||
<h1>Privacy Policy</h1>
|
<h1>Privacy Policy</h1>
|
||||||
<p>Effective Date: <b>1/4/2025</b></p>
|
<p>Effective Date: <b>1/4/2025</b></p>
|
||||||
|
|
||||||
@@ -35,14 +36,9 @@
|
|||||||
We may update this privacy policy from time to time to reflect changes in our practices or for legal compliance. The "Effective Date" above will indicate when the latest changes were made.
|
We may update this privacy policy from time to time to reflect changes in our practices or for legal compliance. The "Effective Date" above will indicate when the latest changes were made.
|
||||||
</p>
|
</p>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
@import url('@/assets/typography.css');
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
<template>
|
|
||||||
<router-view />
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="my-16">
|
<DefaultLayout>
|
||||||
|
<v-container>
|
||||||
<!-- ALPR Verification Dialog -->
|
<!-- ALPR Verification Dialog -->
|
||||||
<ALPRVerificationDialog />
|
<ALPRVerificationDialog />
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@
|
|||||||
<v-img cover :aspect-ratio="1.5" class="mx-auto mt-5" src="/id.webp" style="width: 90%; border-radius: 8px;" />
|
<v-img cover :aspect-ratio="1.5" class="mx-auto mt-5" src="/id.webp" style="width: 90%; border-radius: 8px;" />
|
||||||
|
|
||||||
<v-card-text class="text-body-1">
|
<v-card-text class="text-body-1">
|
||||||
<p class="mb-4 sans-serif">The <b>OSM Web Editor</b> provides a more advanced interface for detailed reporting in your web browser.</p>
|
<p class="mb-4 sans-serif">The <b>OSM Web Editor</b> provides a more advanced interface for detailed reporting in your browser.</p>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-actions class="pa-4">
|
<v-card-actions class="pa-4">
|
||||||
@@ -104,9 +105,11 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import ALPRVerificationDialog from '@/components/ALPRVerificationDialog.vue';
|
import ALPRVerificationDialog from '@/components/ALPRVerificationDialog.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<Hero
|
<DefaultLayout>
|
||||||
imageUrl="/id.webp"
|
<template #header>
|
||||||
title="Submit Cameras"
|
<Hero
|
||||||
description="Add or edit ALPRs using OSM's powerful web-based editor."
|
imageUrl="/id.webp"
|
||||||
/>
|
title="Submit Cameras"
|
||||||
|
description="Add or edit ALPRs using OSM's powerful web-based editor."
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container class="mb-16">
|
<v-container>
|
||||||
<h1 class="text-center">
|
<h1 class="text-center">
|
||||||
Editing the Map
|
Editing the Map
|
||||||
</h1>
|
</h1>
|
||||||
@@ -47,7 +50,18 @@
|
|||||||
Once you've found the location of the ALPR, click the <strong>Edit</strong> button in the top left corner of the page. This will open the OpenStreetMap editor, where you can add the ALPR to the map.
|
Once you've found the location of the ALPR, click the <strong>Edit</strong> button in the top left corner of the page. This will open the OpenStreetMap editor, where you can add the ALPR to the map.
|
||||||
</p>
|
</p>
|
||||||
<v-img max-width="450" src="/edit-map.png" class="my-8" />
|
<v-img max-width="450" src="/edit-map.png" class="my-8" />
|
||||||
<p class="mt-16 mb-8">
|
|
||||||
|
<v-alert
|
||||||
|
variant="tonal"
|
||||||
|
type="warning"
|
||||||
|
class="mt-16 mb-6"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Add cameras as <strong>standalone points only</strong>! Do not connect them to roads, buildings, or other objects. Place the point exactly where the camera is physically located, but keep it as an independent point on the map.
|
||||||
|
</p>
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
|
<p class="mb-8">
|
||||||
To add the ALPR, click the <strong>Point</strong> button at the top center of the editor, then click on the location of the ALPR on the map. In the popup that appears, paste one of the following sets of tags based on the brand of the ALPR:
|
To add the ALPR, click the <strong>Point</strong> button at the top center of the editor, then click on the location of the ALPR on the map. In the popup that appears, paste one of the following sets of tags based on the brand of the ALPR:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -113,9 +127,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-stepper-vertical>
|
</v-stepper-vertical>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import OSMTagSelector from '@/components/OSMTagSelector.vue';
|
import OSMTagSelector from '@/components/OSMTagSelector.vue';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="mb-16">
|
<DefaultLayout>
|
||||||
|
<v-container class="narrow-text">
|
||||||
<h1>Terms of Service for DeFlock</h1>
|
<h1>Terms of Service for DeFlock</h1>
|
||||||
<p>Effective Date: <b>12/26/2024</b></p>
|
<p>Effective Date: <b>12/26/2024</b></p>
|
||||||
|
|
||||||
@@ -88,14 +89,9 @@
|
|||||||
Email: <a href="mailto:contact@deflock.me">contact@deflock.me</a>
|
Email: <a href="mailto:contact@deflock.me">contact@deflock.me</a>
|
||||||
</p>
|
</p>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
</DefaultLayout>
|
||||||
<Footer />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
@import url('@/assets/typography.css');
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<Hero
|
<DefaultLayout>
|
||||||
image-url="/flock-camera.jpeg"
|
<template #header>
|
||||||
:opacity="0"
|
<Hero
|
||||||
/>
|
image-url="/flock-camera.jpeg"
|
||||||
|
:opacity="0"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-container class="info-section mb-16">
|
<v-container class="info-section mb-16">
|
||||||
<h1 class="mt-0">What are ALPRs?</h1>
|
<h1 class="mt-0">What are ALPRs?</h1>
|
||||||
@@ -43,20 +46,18 @@
|
|||||||
|
|
||||||
<SimilarProjects id="similar" />
|
<SimilarProjects id="similar" />
|
||||||
</v-container>
|
</v-container>
|
||||||
<Footer />
|
</DefaultLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import Hero from '@/components/layout/Hero.vue';
|
import Hero from '@/components/layout/Hero.vue';
|
||||||
import Dangers from '@/components/Dangers.vue';
|
import Dangers from '@/components/Dangers.vue';
|
||||||
import FAQ from '@/components/FAQ.vue';
|
import FAQ from '@/components/FAQ.vue';
|
||||||
import Footer from '@/components/layout/Footer.vue';
|
|
||||||
import SimilarProjects from '@/components/SimilarProjects.vue';
|
import SimilarProjects from '@/components/SimilarProjects.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import url('@/assets/typography.css');
|
|
||||||
|
|
||||||
h1, h2 {
|
h1, h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user