Skip to main content

Overview

The Projects module helps you organize and track client projects throughout their lifecycle. Each project can include multiple photos, category classification, client association, and detailed project information.

Key Features

Multi-Photo Upload

Upload and store multiple project photos in Supabase Storage

Client Linking

Associate projects with clients from your directory

Category Organization

Categorize projects for better organization and filtering

Full CRUD

Create, read, update, and delete project records

Creating a New Project

1

Navigate to Projects

Access the Projects module from the sidebar menu.
2

Click 'New Project'

Click the “Crear Proyecto” button to open the project creation form.
3

Fill Project Details

Required fields:
  • Name (nombre): Project title or name
  • Client (cliente): Select from existing clients
  • Category (categoria): Project category ID
  • Photos (fotos): Upload one or more images
Optional fields:
  • Additional project metadata
4

Submit the Form

The system will:
  • Generate the next project ID
  • Upload photos to category-specific folders
  • Create the project record in the database

Photo Upload System

Project photos are stored in Supabase Storage with organized paths based on category:
proyectos.php
$bucket = "clientes";
$fotos_urls = [];

if (!empty($_FILES['fotos']['name'][0])) {
    foreach ($_FILES['fotos']['name'] as $index => $nombre_archivo) {
        if ($_FILES['fotos']['error'][$index] === UPLOAD_ERR_OK) {
            // Get category name for bucket path
            $categoria_name_for_bucket = 'general';
            if (isset($categoria_map[$selected_cat_id]) && !empty($categoria_map[$selected_cat_id]['name'])) {
                $categoria_name_for_bucket = $categoria_map[$selected_cat_id]['name'];
            }
            $categoria_segura = preg_replace('/[^A-Za-z0-9_-]/', '_', strtolower($categoria_name_for_bucket));
            $nombre_seguro = preg_replace('/[^A-Za-z0-9_.-]/', '_', basename($nombre_archivo));
            $ruta_supabase = "$categoria_segura/proyectos/$nombre_seguro";
            
            $contenido = file_get_contents($_FILES['fotos']['tmp_name'][$index]);
            
            $ch = curl_init();
            curl_setopt_array($ch, [
                CURLOPT_URL => "$supabase_url/storage/v1/object/$bucket/$ruta_supabase",
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_CUSTOMREQUEST => "PUT",
                CURLOPT_POSTFIELDS => $contenido,
                CURLOPT_HTTPHEADER => [
                    "apikey: $supabase_key",
                    "Authorization: Bearer $supabase_key",
                    "Content-Type: application/octet-stream"
                ]
            ]);
            $upload_response = curl_exec($ch);
            $upload_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            
            if ($upload_code === 200 || $upload_code === 201) {
                $fotos_urls[] = $ruta_supabase;
            }
        }
    }
}

Storage Structure

clientes/
  ├── categoria1/
  │   └── proyectos/
  │       ├── project1_photo1.jpg
  │       ├── project1_photo2.jpg
  │       └── project2_photo1.png
  └── categoria2/
      └── proyectos/
          └── project3_photo1.jpg

Project ID Generation

Projects use auto-incrementing IDs:
proyectos.php
// Get last project ID
$res_ultimo = supabase_request('GET', "$table_name?select=id&order=id.desc&limit=1");
$ultimoProyecto = ($res_ultimo['http'] >= 200 && $res_ultimo['http'] < 300 && is_array($res_ultimo['json'])) ? $res_ultimo['json'] : [];
$ultimoId = !empty($ultimoProyecto) ? intval($ultimoProyecto[0]['id']) : 0;
$nuevoId = $ultimoId + 1;

Project Record Structure

{
  "id": 1,
  "nombre": "Website Redesign",
  "cliente": "Company ABC",
  "categoria": 3,
  "fotos": [
    "web_design/proyectos/mockup1.jpg",
    "web_design/proyectos/mockup2.jpg"
  ],
  "created_at": "2024-01-20T14:30:00Z"
}

Pagination

Project listings support pagination:
proyectos.php
$page = intval($_GET['page'] ?? 1);
$perPage = intval($_GET['rowsPerPage'] ?? 10);
$offset = ($page - 1) * $perPage;
Configuration:
  • Default rows per page: 10
  • URL parameters: ?page=2&rowsPerPage=20
  • User configurable: Yes

Viewing Projects

Project details include:
  • Basic Info: Name, client, category
  • Photo Gallery: All uploaded project images
  • Timestamps: Creation and modification dates
  • Metadata: Any additional project details
Projects are displayed in reverse chronological order by default (newest first).

Editing Projects

1

Select Project

Click the edit button on the project you want to modify.
2

Update Information

Modify any of the following:
  • Project name
  • Client association
  • Category assignment
  • Add or remove photos
3

Save Changes

Submit the form to update the database record.

Deleting Projects

Deleting a project does not automatically remove associated photos from Supabase Storage. Consider implementing cleanup routines.
To delete a project:
  1. Click the delete button
  2. Confirm the deletion
  3. The project record is permanently removed

Client Integration

Projects are linked to clients in the Clients module. When viewing a client, you can filter to see all their associated projects.

Client Selection

When creating a project, select from the dropdown of existing clients. The client name is stored as a string reference in the project record.

Category Organization

Projects must be assigned to a category. Categories are managed in the Content module and help organize projects by:
  • Service type (Web Design, Branding, Development)
  • Industry sector
  • Project scope
  • Custom classifications

Database Table

Table Name: Proyectos
ColumnTypeDescription
idintegerAuto-incrementing primary key
nombretextProject name/title
clientetextClient name reference
categoriaintegerCategory ID
fotostext[]Array of storage paths
created_attimestamptzCreation timestamp

API Endpoints

The Projects module uses Supabase REST API:
  • GET /rest/v1/Proyectos - List projects
  • POST /rest/v1/Proyectos - Create project
  • PATCH /rest/v1/Proyectos?id=eq.{id} - Update project
  • DELETE /rest/v1/Proyectos?id=eq.{id} - Delete project

Best Practices

Optimize images before upload to reduce storage costs and improve page load times.
Use descriptive project names that include the client name for easier searching.
Regularly audit and remove unused photos from Supabase Storage to manage costs.

Troubleshooting

Photo Upload Fails

Causes:
  • File size exceeds limits
  • Invalid file format
  • Storage bucket permissions
Solution: Check PHP upload_max_filesize, post_max_size, and Supabase Storage policies.

Category Not Found

Problem: “Selecciona una categoría válida” error Solution: Ensure the selected category ID exists in the categoria-servicios table.

Client Association Issues

Problem: Client name not displaying Solution: Verify the client name exactly matches a record in the clientes table.

Next Steps

Clients

Manage client directory

Dashboard

View project statistics