Desarrolla una solución completa para la gestión de contratos utilizando las tecnologías más modernas del desarrollo web.
Interfaz intuitiva para crear contratos personalizados con plantillas predefinidas y campos dinámicos.
Encuentra contratos rápidamente utilizando filtros avanzados y búsqueda por palabras clave.
Sistema de firma digital con validación y seguimiento del estado de cada contrato.
Sistema de alertas para recordatorios de vencimiento y acciones pendientes.
Genera reportes detallados y visualiza métricas importantes sobre tus contratos.
Protección de datos con encriptación, autenticación y control de acceso basado en roles.
Desarrollamos un servidor robusto y escalable utilizando Node.js y Express.js, con las siguientes características:
Interfaz de usuario moderna y reactiva desarrollada con Vue.js y sus ecosistema:
Definimos los requisitos del sistema, casos de uso y arquitectura general.
Configuramos el servidor Express, conexión a base de datos y modelos de datos.
// server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
// Middlewares
app.use(cors());
app.use(express.json());
// Conexión a MongoDB
mongoose.connect('mongodb://localhost:27017/contracts', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Rutas
app.use('/api/contracts', require('./routes/contracts'));
app.use('/api/auth', require('./routes/auth'));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Servidor ejecutándose en puerto ${PORT}`);
});
Implementamos endpoints para gestión de contratos, usuarios y autenticación.
// routes/contracts.js
const express = require('express');
const router = express.Router();
const Contract = require('../models/Contract');
const auth = require('../middleware/auth');
// Obtener todos los contratos
router.get('/', auth, async (req, res) => {
try {
const contracts = await Contract.find({ user: req.user.id });
res.json(contracts);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Crear nuevo contrato
router.post('/', auth, async (req, res) => {
const contract = new Contract({
title: req.body.title,
description: req.body.description,
parties: req.body.parties,
terms: req.body.terms,
user: req.user.id
});
try {
const newContract = await contract.save();
res.status(201).json(newContract);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
Configuramos Vue.js, Vue Router y Vuex para la gestión del estado.
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App)
.use(store)
.use(router)
.mount('#app')
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
user: null,
contracts: [],
loading: false
},
mutations: {
SET_USER(state, user) {
state.user = user
},
SET_CONTRACTS(state, contracts) {
state.contracts = contracts
}
},
actions: {
async login({ commit }, credentials) {
// Lógica de autenticación
},
async fetchContracts({ commit }) {
// Obtener contratos desde la API
}
}
})
Desarrollamos componentes reutilizables para la interfaz de usuario.
<template>
<div class="contract-list">
<div class="header">
<h2>Mis Contratos</h2>
<button @click="showCreateModal = true" class="btn btn-primary">
Nuevo Contrato
</button>
</div>
<div class="filters">
<input v-model="searchTerm" placeholder="Buscar contratos...">
<select v-model="statusFilter">
<option value="">Todos</option>
<option value="active">Activos</option>
<option value="pending">Pendientes</option>
<option value="expired">Expirados</option>
</select>
</div>
<div class="contracts-grid">
<ContractCard
v-for="contract in filteredContracts"
:key="contract._id"
:contract="contract"
@edit="editContract"
@delete="deleteContract"
/>
</div>
<CreateContractModal
v-if="showCreateModal"
@close="showCreateModal = false"
@created="fetchContracts"
/>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
import { useStore } from 'vuex'
import ContractCard from '@/components/ContractCard.vue'
import CreateContractModal from '@/components/CreateContractModal.vue'
export default {
name: 'ContractList',
components: {
ContractCard,
CreateContractModal
},
setup() {
const store = useStore()
const searchTerm = ref('')
const statusFilter = ref('')
const showCreateModal = ref(false)
const contracts = computed(() => store.state.contracts)
const filteredContracts = computed(() => {
return contracts.value.filter(contract => {
const matchesSearch = contract.title.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
contract.description.toLowerCase().includes(searchTerm.value.toLowerCase())
const matchesStatus = !statusFilter.value || contract.status === statusFilter.value
return matchesSearch && matchesStatus
})
})
const fetchContracts = async () => {
await store.dispatch('fetchContracts')
}
onMounted(() => {
fetchContracts()
})
return {
searchTerm,
statusFilter,
showCreateModal,
filteredContracts,
fetchContracts
}
}
}
</script>
Conectamos frontend y backend, realizamos pruebas integrales y despliegue.
// services/api.js
import axios from 'axios'
const API_URL = process.env.VUE_APP_API_URL || 'http://localhost:5000/api'
const api = axios.create({
baseURL: API_URL
})
// Interceptor para agregar token de autenticación
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// Servicios para contratos
export const contractsService = {
getAll: () => api.get('/contracts'),
getById: (id) => api.get(`/contracts/${id}`),
create: (data) => api.post('/contracts', data),
update: (id, data) => api.put(`/contracts/${id}`, data),
delete: (id) => api.delete(`/contracts/${id}`),
sign: (id, signature) => api.post(`/contracts/${id}/sign`, { signature })
}
export default api