Skip to main content

Installation Guide

This comprehensive guide will walk you through setting up Portal Ciudadano Manta from scratch, including all prerequisites, environment configuration, and Supabase backend setup.

Prerequisites

Before you begin, ensure your development environment meets these requirements:

Node.js

Version 16 or higherCheck your version:
node --version
Download from nodejs.org

Package Manager

npm (included with Node.js) or yarnCheck your version:
npm --version

Git

Latest versionCheck your version:
git --version
Download from git-scm.com

Supabase Account

Free tier availableSign up at supabase.com
Minimum System Requirements:
  • 4GB RAM
  • 2GB free disk space
  • Modern web browser (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)

Step 1: Clone the Repository

First, clone the project repository to your local machine:
git clone https://github.com/joaoelian204/Portal-Ciudadano-Manta-web.git
cd portalCiudadanoManta
The repository includes all source code, configuration files, and assets needed to run the application.

Step 2: Install Dependencies

Install all required npm packages:
npm install

Key Dependencies Installed

The installation includes:
package.json
{
  "vue": "^3.5.22",              // Progressive JavaScript framework
  "vue-router": "^4.6.3",        // Official routing
  "pinia": "^3.0.3"              // State management
}
package.json
{
  "typescript": "~5.9.3",        // Type safety
  "vite": "^7.1.7",              // Ultra-fast build tool
  "@vitejs/plugin-vue": "^6.0.1", // Vite Vue plugin
  "vue-tsc": "^3.1.0"            // TypeScript checker
}
package.json
{
  "tailwindcss": "^4.1.14",           // Utility-first CSS
  "@tailwindcss/vite": "^4.1.14",    // Vite integration
  "@fortawesome/fontawesome-free": "^7.1.0"  // Icons
}
package.json
{
  "@supabase/supabase-js": "^2.76.1"  // Supabase client
}
package.json
{
  "leaflet": "^1.9.4",           // Interactive maps
  "vue3-leaflet": "^1.0.50",     // Vue 3 Leaflet wrapper
  "@types/leaflet": "^1.9.21"    // TypeScript types
}
package.json
{
  "vue-i18n": "^9.14.5"  // i18n for Spanish/English/Kichwa
}
package.json
{
  "jspdf": "^3.0.4",              // PDF creation
  "jspdf-autotable": "^5.0.2"     // PDF tables
}

Step 3: Supabase Project Setup

Supabase provides the backend infrastructure including database, authentication, and file storage.
1

Create Supabase Project

  1. Go to supabase.com and sign in
  2. Click “New Project”
  3. Fill in project details:
    • Name: Portal-Ciudadano-Manta
    • Database Password: Strong password (save this!)
    • Region: Choose closest to Ecuador (e.g., US East)
    • Pricing Plan: Free tier is sufficient for development
  4. Click “Create new project”
Project creation takes 1-2 minutes. You’ll see a loading screen while Supabase provisions your PostgreSQL database.
2

Get API Credentials

Once the project is ready:
  1. Go to SettingsAPI
  2. Copy these values:
    • Project URL (VITE_SUPABASE_URL)
    • anon public key (VITE_SUPABASE_ANON_KEY)
Keep your service_role key secret! Only use the anon key in your frontend application.
3

Create Database Schema

Navigate to SQL Editor in your Supabase dashboard and execute the following schema:
Database Schema
-- Enable required extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "postgis";  -- For geolocation

-- Users table (ciudadanos)
CREATE TABLE usuarios (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  email TEXT UNIQUE NOT NULL,
  nombres TEXT NOT NULL,
  apellidos TEXT NOT NULL,
  cedula TEXT UNIQUE NOT NULL CHECK (length(cedula) = 10),
  parroquia TEXT NOT NULL,
  barrio TEXT NOT NULL,
  tipo TEXT NOT NULL DEFAULT 'ciudadano' CHECK (tipo IN ('ciudadano', 'administrador')),
  activo BOOLEAN DEFAULT true,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Administrators table
CREATE TABLE administradores (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  email TEXT UNIQUE NOT NULL,
  nombres TEXT NOT NULL,
  apellidos TEXT NOT NULL,
  cedula TEXT UNIQUE NOT NULL CHECK (length(cedula) = 10),
  activo BOOLEAN DEFAULT true,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- News table
CREATE TABLE noticias (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  titulo TEXT NOT NULL,
  contenido TEXT NOT NULL,
  resumen TEXT,
  imagen_url TEXT,
  categoria TEXT NOT NULL,
  autor_id UUID REFERENCES administradores(id),
  publicada BOOLEAN DEFAULT false,
  fecha_publicacion TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Surveys table
CREATE TABLE encuestas (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  titulo TEXT NOT NULL,
  descripcion TEXT NOT NULL,
  tipo TEXT NOT NULL CHECK (tipo IN ('opcion_multiple', 'abierta', 'calificacion')),
  opciones JSONB,
  fecha_inicio TIMESTAMPTZ NOT NULL,
  fecha_fin TIMESTAMPTZ NOT NULL,
  activa BOOLEAN DEFAULT true,
  parroquia_destino TEXT,
  barrio_destino TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Survey responses table
CREATE TABLE respuestas_encuestas (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  encuesta_id UUID REFERENCES encuestas(id) ON DELETE CASCADE,
  usuario_id UUID REFERENCES usuarios(id) ON DELETE CASCADE,
  respuesta JSONB NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(encuesta_id, usuario_id)  -- One response per user per survey
);

-- Reports table with PostGIS geometry
CREATE TABLE reportes (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  usuario_id UUID REFERENCES usuarios(id) ON DELETE CASCADE,
  categoria TEXT NOT NULL CHECK (categoria IN (
    'alumbrado', 'baches', 'limpieza', 'agua', 'alcantarillado',
    'parques', 'señalizacion', 'seguridad', 'ruido', 'otro'
  )),
  descripcion TEXT NOT NULL,
  ubicacion GEOGRAPHY(POINT, 4326) NOT NULL,  -- PostGIS for location
  direccion TEXT,
  fotos TEXT[],  -- Array of image URLs
  estado TEXT NOT NULL DEFAULT 'pendiente' CHECK (estado IN (
    'pendiente', 'en_revision', 'en_proceso', 'resuelto', 'rechazado'
  )),
  prioridad TEXT DEFAULT 'media' CHECK (prioridad IN (
    'baja', 'media', 'alta', 'urgente'
  )),
  notas_admin TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Create indexes for performance
CREATE INDEX idx_reportes_usuario ON reportes(usuario_id);
CREATE INDEX idx_reportes_estado ON reportes(estado);
CREATE INDEX idx_reportes_categoria ON reportes(categoria);
CREATE INDEX idx_reportes_ubicacion ON reportes USING GIST(ubicacion);
CREATE INDEX idx_noticias_publicada ON noticias(publicada);
CREATE INDEX idx_encuestas_activa ON encuestas(activa);

-- Auto-update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_usuarios_updated_at BEFORE UPDATE ON usuarios
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_administradores_updated_at BEFORE UPDATE ON administradores
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_noticias_updated_at BEFORE UPDATE ON noticias
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_encuestas_updated_at BEFORE UPDATE ON encuestas
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_reportes_updated_at BEFORE UPDATE ON reportes
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
Click “Run” to execute the schema. This creates all tables, indexes, and triggers.
4

Configure Row Level Security (RLS)

Enable RLS to secure your data. In the SQL Editor, run:
Row Level Security
-- Enable RLS on all tables
ALTER TABLE usuarios ENABLE ROW LEVEL SECURITY;
ALTER TABLE administradores ENABLE ROW LEVEL SECURITY;
ALTER TABLE noticias ENABLE ROW LEVEL SECURITY;
ALTER TABLE encuestas ENABLE ROW LEVEL SECURITY;
ALTER TABLE respuestas_encuestas ENABLE ROW LEVEL SECURITY;
ALTER TABLE reportes ENABLE ROW LEVEL SECURITY;

-- USUARIOS: Users can read their own data
CREATE POLICY "Users can view own profile" ON usuarios
  FOR SELECT USING (auth.uid() = id);

CREATE POLICY "Users can update own profile" ON usuarios
  FOR UPDATE USING (auth.uid() = id);

-- NOTICIAS: Public read, admin write
CREATE POLICY "Anyone can view published news" ON noticias
  FOR SELECT USING (publicada = true);

CREATE POLICY "Admins can manage news" ON noticias
  FOR ALL USING (
    EXISTS (
      SELECT 1 FROM administradores
      WHERE id = auth.uid() AND activo = true
    )
  );

-- ENCUESTAS: Users can view active surveys for their location
CREATE POLICY "Users can view active surveys" ON encuestas
  FOR SELECT USING (
    activa = true
    AND NOW() BETWEEN fecha_inicio AND fecha_fin
    AND (
      parroquia_destino IS NULL
      OR parroquia_destino IN (
        SELECT parroquia FROM usuarios WHERE id = auth.uid()
      )
    )
  );

CREATE POLICY "Admins can manage surveys" ON encuestas
  FOR ALL USING (
    EXISTS (
      SELECT 1 FROM administradores
      WHERE id = auth.uid() AND activo = true
    )
  );

-- RESPUESTAS_ENCUESTAS: Users can submit and view own responses
CREATE POLICY "Users can create own responses" ON respuestas_encuestas
  FOR INSERT WITH CHECK (auth.uid() = usuario_id);

CREATE POLICY "Users can view own responses" ON respuestas_encuestas
  FOR SELECT USING (auth.uid() = usuario_id);

CREATE POLICY "Admins can view all responses" ON respuestas_encuestas
  FOR SELECT USING (
    EXISTS (
      SELECT 1 FROM administradores
      WHERE id = auth.uid() AND activo = true
    )
  );

-- REPORTES: Users can manage own reports, admins can view all
CREATE POLICY "Users can create own reports" ON reportes
  FOR INSERT WITH CHECK (auth.uid() = usuario_id);

CREATE POLICY "Users can view own reports" ON reportes
  FOR SELECT USING (auth.uid() = usuario_id);

CREATE POLICY "Users can update own reports" ON reportes
  FOR UPDATE USING (auth.uid() = usuario_id);

CREATE POLICY "Admins can view all reports" ON reportes
  FOR SELECT USING (
    EXISTS (
      SELECT 1 FROM administradores
      WHERE id = auth.uid() AND activo = true
    )
  );

CREATE POLICY "Admins can update all reports" ON reportes
  FOR UPDATE USING (
    EXISTS (
      SELECT 1 FROM administradores
      WHERE id = auth.uid() AND activo = true
    )
  );
Row Level Security ensures users can only access data they’re authorized to see. Admins have full access, while citizens can only access their own data and public content.
5

Configure Storage Buckets

For image uploads (news images, report photos):
  1. Go to Storage in Supabase dashboard
  2. Create a new bucket called reportes-fotos
  3. Set it to Public
  4. Create another bucket called noticias-imagenes
  5. Set it to Public
Then configure storage policies in SQL Editor:
Storage Policies
-- Allow authenticated users to upload report photos
CREATE POLICY "Users can upload report photos"
  ON storage.objects FOR INSERT
  TO authenticated
  WITH CHECK (bucket_id = 'reportes-fotos');

-- Allow public read access to report photos
CREATE POLICY "Public can view report photos"
  ON storage.objects FOR SELECT
  TO public
  USING (bucket_id = 'reportes-fotos');

-- Allow admins to upload news images
CREATE POLICY "Admins can upload news images"
  ON storage.objects FOR INSERT
  TO authenticated
  WITH CHECK (
    bucket_id = 'noticias-imagenes'
    AND EXISTS (
      SELECT 1 FROM administradores
      WHERE id = auth.uid() AND activo = true
    )
  );

-- Allow public read access to news images
CREATE POLICY "Public can view news images"
  ON storage.objects FOR SELECT
  TO public
  USING (bucket_id = 'noticias-imagenes');
6

Configure Authentication Settings

Go to AuthenticationSettings:Email Auth Settings:
  • Enable Email confirmation
  • Set Site URL: Your production URL (or http://localhost:5173 for dev)
  • Add Redirect URLs:
    • http://localhost:5173/login?verified=true
    • http://localhost:5173/reset-password
    • Your production URLs
Email Templates (customize as needed):
  • Confirmation email
  • Password reset email
  • Magic link email
The site URL and redirect URLs must match your application URLs exactly for authentication flows to work properly.

Step 4: Environment Configuration

Create your environment file with the Supabase credentials:
touch .env
Security Best Practices:
  • Never commit .env to version control
  • Use different projects for development and production
  • Rotate keys periodically
  • Never expose the service_role key in frontend code

How Environment Variables Are Used

The app loads these variables in the Supabase client:
src/lib/supabase.ts
import { createClient } from "@supabase/supabase-js";
import type { Database } from "../types/database.types";

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseAnonKey) {
  console.warn(
    "⚠️ Missing Supabase credentials. Please configure VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY in .env"
  );
}

export const supabase = createClient<Database>(
  supabaseUrl || "https://placeholder.supabase.co",
  supabaseAnonKey || "placeholder-key",
  {
    auth: {
      persistSession: true,
      autoRefreshToken: true,
      detectSessionInUrl: true,
      flowType: "pkce",  // PKCE flow for enhanced security
    },
    db: {
      schema: "public",
    },
  }
);

Step 5: Start Development Server

With everything configured, start the development server:
npm run dev
The application should now be running at http://localhost:5173
You should see output like:
  VITE v7.1.7  ready in 423 ms

  Local:   http://localhost:5173/
  Network: use --host to expose
  press h + enter to show help

Step 6: Verify Installation

Test that everything is working:
1

Check Browser Console

Open DevTools (F12) and verify:
  • No errors in console
  • Supabase client initialized successfully
  • No 404s for assets
2

Test Registration

Navigate to /register and create a test account to verify:
  • Form validation works
  • Supabase authentication works
  • User record created in database
  • Confirmation email sent
3

Test Login

Go to /login and sign in with your test account:
  • Authentication succeeds
  • Redirects to dashboard
  • User data loads correctly

Building for Production

When ready to deploy:
1

Run Type Check

npm run build
This runs TypeScript validation and builds the production bundle to dist/
2

Preview Production Build

npm run preview
Test the production build locally before deploying.
3

Deploy to Netlify

Portal Ciudadano Manta includes a netlify.toml configuration for easy deployment:Option 1: Netlify CLI
npm install -g netlify-cli
netlify login
netlify init
netlify deploy --prod
Option 2: GitHub Integration
  1. Push your code to GitHub
  2. Connect repository in Netlify dashboard
  3. Configure build settings:
    • Build command: npm run build
    • Publish directory: dist
    • Node version: 20
  4. Add environment variables in Netlify:
    • VITE_SUPABASE_URL
    • VITE_SUPABASE_ANON_KEY
The included netlify.toml configures redirects for SPA routing, security headers, and caching automatically.

Troubleshooting

Symptoms: “Failed to fetch”, timeout errors, or authentication issuesSolutions:
  1. Verify .env file exists and has correct credentials
  2. Check Supabase project status (not paused)
  3. Verify API keys are copied correctly (no extra spaces)
  4. Check browser console for specific error messages
  5. Ensure Supabase project URL uses HTTPS
Symptoms: TypeScript errors, missing modulesSolutions:
  1. Delete node_modules and reinstall:
    rm -rf node_modules package-lock.json
    npm install
    
  2. Clear Vite cache:
    rm -rf node_modules/.vite
    
  3. Check Node.js version (must be 16+)
Symptoms: Blank map, missing tilesSolutions:
  1. Check browser console for CORS errors
  2. Verify Leaflet CSS is imported in main.ts
  3. Check network tab for tile loading issues
  4. Ensure location permissions are granted
Symptoms: Can’t log in, session expires immediatelySolutions:
  1. Verify redirect URLs in Supabase settings
  2. Check site URL configuration
  3. Clear browser cookies and local storage
  4. Verify RLS policies are correct
  5. Check email confirmation settings

Next Steps

Quickstart Guide

Learn how to use the platform features

API Reference

Explore the Supabase database schema and APIs

Deployment

Deploy to production (Netlify, Vercel, etc.)

Architecture

Learn about the system architecture
Installation complete! Your Portal Ciudadano Manta development environment is ready.

Build docs developers (and LLMs) love