Initial Commit with ENVs, directory structures, cursor rules, etc

This commit is contained in:
2025-05-10 17:12:58 -05:00
commit 1a65e69d25
21 changed files with 572 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
---
description:
globs: *.php,*.js,*.css
alwaysApply: false
---
.php files should use the following naming conventions:
"classes": "PascalCase",
"functions": "camelCase",
"variables": "camelCase",
"constants": "UPPER_SNAKE_CASE"
.js files should use the following naming conventions:
"functions": "camelCase",
"variables": "camelCase",
"constants": "UPPER_SNAKE_CASE"
.css files should us the following naming conventions:
"ids": "camelCase",
"classes": "kebab-case"
+8
View File
@@ -0,0 +1,8 @@
---
description:
globs:
alwaysApply: true
---
This is a zero build project. We are not allowing packages to be installed or suggested for this project. No dependendcies on the backend/server side. On the front-end we will only allow CDN js and css files or libraries.
The following are allowed on the front-end: bootstrap|fontawesome|jquery
+22
View File
@@ -0,0 +1,22 @@
# Data directory
/data/
/data/*.json
# Exports directory
/exports/
/exports/*.json
/exports/export_log.txt
# Environment files
.env
.env.*
!.env.example
# IDE files
.idea/
.vscode/
*.sublime-*
# OS files
.DS_Store
Thumbs.db
+15
View File
@@ -0,0 +1,15 @@
# Protect data directory
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^data/.* - [F,L]
RewriteRule ^exports/.* - [F,L]
</IfModule>
# Disable directory browsing
Options -Indexes
# Deny access to hidden files and directories
<FilesMatch "^\.">
Order allow,deny
Deny from all
</FilesMatch>
+59
View File
@@ -0,0 +1,59 @@
/* Main Styles */
:root {
--primary-color: #4a90e2;
--secondary-color: #f5f5f5;
--text-color: #333;
--border-color: #ddd;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
color: var(--text-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Tab Styles */
.tabs {
display: flex;
border-bottom: 1px solid var(--border-color);
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border: 1px solid transparent;
border-bottom: none;
margin-right: 5px;
}
.tab.active {
background: white;
border-color: var(--border-color);
border-bottom: 1px solid white;
margin-bottom: -1px;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.container {
padding: 10px;
}
.tabs {
flex-direction: column;
}
.tab {
margin-right: 0;
margin-bottom: 5px;
}
}
+25
View File
@@ -0,0 +1,25 @@
document.addEventListener('DOMContentLoaded', function() {
// Tab functionality
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
// Remove active class from all tabs
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(content => content.style.display = 'none');
// Add active class to clicked tab
tab.classList.add('active');
// Show corresponding content
const targetId = tab.getAttribute('data-target');
document.getElementById(targetId).style.display = 'block';
});
});
// Initialize first tab as active
if (tabs.length > 0) {
tabs[0].click();
}
});
+41
View File
@@ -0,0 +1,41 @@
<?php
require_once __DIR__ . '/env.php';
// Load environment variables
Env::load();
// Paths
define('ROOT_PATH', dirname(__DIR__));
define('DATA_PATH', ROOT_PATH . '/data');
define('EXPORT_PATH', ROOT_PATH . '/exports');
// Google API Configuration
define('GOOGLE_CLIENT_ID', Env::get('GOOGLE_CLIENT_ID'));
define('GOOGLE_CLIENT_SECRET', Env::get('GOOGLE_CLIENT_SECRET'));
define('GOOGLE_REDIRECT_URI', Env::get('GOOGLE_REDIRECT_URI'));
define('GOOGLE_CALENDAR_ID', Env::get('GOOGLE_CALENDAR_ID'));
define('GOOGLE_CALENDAR_EMBED_CODE', Env::get('GOOGLE_CALENDAR_EMBED_CODE'));
define('GOOGLE_DRIVE_FOLDER_ID', Env::get('GOOGLE_DRIVE_FOLDER_ID'));
// Application Settings
define('APP_ENV', Env::get('APP_ENV', 'production'));
define('APP_DEBUG', Env::get('APP_DEBUG', 'false') === 'true');
define('APP_URL', Env::get('APP_URL', 'http://localhost/family-hub'));
// Export settings
define('EXPORT_DESTINATION', EXPORT_PATH);
define('EXPORT_FREQUENCY', Env::get('EXPORT_FREQUENCY', 'daily'));
define('EXPORT_RETENTION_DAYS', (int)Env::get('EXPORT_RETENTION_DAYS', 30));
// Tab configuration
$TABS = [
'chores' => ['title' => 'Chores', 'icon' => 'tasks'],
'groceries' => ['title' => 'Grocery List', 'icon' => 'shopping-cart'],
'meals' => ['title' => 'Meal Plan', 'icon' => 'utensils']
];
// Load local configuration if exists
if (file_exists(__DIR__ . '/local.php')) {
include __DIR__ . '/local.php';
}
?>
+41
View File
@@ -0,0 +1,41 @@
<?php
class Env {
private static $variables = [];
public static function load() {
$envFile = dirname(__DIR__) . '/.env';
if (!file_exists($envFile)) {
throw new Exception('.env file not found. Please create one based on .env.example');
}
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
// Skip comments
if (strpos(trim($line), '#') === 0) {
continue;
}
list($name, $value) = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
// Remove quotes if present
if (strpos($value, '"') === 0 || strpos($value, "'") === 0) {
$value = substr($value, 1, -1);
}
self::$variables[$name] = $value;
putenv("$name=$value");
}
}
public static function get($key, $default = null) {
return self::$variables[$key] ?? $default;
}
public static function all() {
return self::$variables;
}
}
+20
View File
@@ -0,0 +1,20 @@
# Google API Credentials
GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here
GOOGLE_REDIRECT_URI=http://localhost/family-hub/auth/google/callback
# Google Calendar
GOOGLE_CALENDAR_ID=your_calendar_id_here
GOOGLE_CALENDAR_EMBED_CODE=your_embed_code_here
# Google Drive
GOOGLE_DRIVE_FOLDER_ID=your_folder_id_here
# Application Settings
APP_ENV=development
APP_DEBUG=true
APP_URL=http://localhost/family-hub
# Export Settings
EXPORT_FREQUENCY=daily
EXPORT_RETENTION_DAYS=30
+29
View File
@@ -0,0 +1,29 @@
<?php
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/utils.php';
require_once __DIR__ . '/config/config.php';
function exportData($type) {
ensureExportDirectory();
$data = readJsonFile($type . '.json');
$filename = generateExportFilename($type);
$exportPath = EXPORT_DESTINATION . '/' . $filename;
return file_put_contents($exportPath, json_encode($data, JSON_PRETTY_PRINT));
}
// Handle export request
if (isset($_GET['type'])) {
$type = sanitizeInput($_GET['type']);
if (in_array($type, ['chores', 'groceries', 'meals'])) {
if (exportData($type)) {
echo json_encode(['success' => true, 'message' => 'Export successful']);
} else {
echo json_encode(['success' => false, 'message' => 'Export failed']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid export type']);
}
} else {
echo json_encode(['success' => false, 'message' => 'No export type specified']);
}
+29
View File
@@ -0,0 +1,29 @@
# Data files (contain user data)
/data/*.json
# Export directory
/exports/
# Environment-specific configurations
/config/local.php
# System files
.DS_Store
Thumbs.db
# Editor files
.vscode/
*.swp
*.swo
# Temporary files
*.tmp
*.tmp.*
*.tmp/*
*.tmp/*.*
# Cache files
*.cache
*.cache.*
*.cache/*
*.cache/*.*
+23
View File
@@ -0,0 +1,23 @@
<?php
function readJsonFile($filename) {
$filepath = __DIR__ . '/../data/' . $filename;
if (!file_exists($filepath)) {
return [];
}
$content = file_get_contents($filepath);
return json_decode($content, true) ?? [];
}
function writeJsonFile($filename, $data) {
$filepath = __DIR__ . '/../data/' . $filename;
$json = json_encode($data, JSON_PRETTY_PRINT);
return file_put_contents($filepath, $json);
}
function ensureDataDirectory() {
$dataDir = __DIR__ . '/../data';
if (!file_exists($dataDir)) {
mkdir($dataDir, 0755, true);
}
}
+17
View File
@@ -0,0 +1,17 @@
</main>
<footer class="bg-light p-3 mt-4">
<div class="container text-center">
<p>Family Hub &copy; <?= date('Y') ?></p>
</div>
</footer>
<!-- Bootstrap JS from CDN -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<!-- jQuery from CDN (if needed) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Custom JavaScript -->
<script src="assets/js/main.js"></script>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Family Hub</title>
<!-- Bootstrap CSS from CDN -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome icons from CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Custom styles -->
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header class="bg-primary text-white p-3">
<div class="container">
<h1>Family Hub</h1>
</div>
</header>
<main class="container py-4">
+23
View File
@@ -0,0 +1,23 @@
<?php
require_once __DIR__ . '/../config/config.php';
function sanitizeInput($input) {
if (is_array($input)) {
return array_map('sanitizeInput', $input);
}
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}
function formatDate($date) {
return date('Y-m-d', strtotime($date));
}
function generateExportFilename($type) {
return $type . '_' . date('Y-m-d') . '.json';
}
function ensureExportDirectory() {
if (!file_exists(EXPORT_DESTINATION)) {
mkdir(EXPORT_DESTINATION, 0755, true);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
require_once 'config/config.php';
require_once 'includes/db.php';
require_once 'includes/utils.php';
// Determine which tab is active
$activeTab = isset($_GET['tab']) ? $_GET['tab'] : 'chores';
// Include header
include 'includes/header.php';
?>
<div class="container">
<div class="tabs">
<?php foreach ($TABS as $tabId => $tab): ?>
<a href="?tab=<?= $tabId ?>" class="tab <?= $activeTab === $tabId ? 'active' : '' ?>">
<i class="fa fa-<?= $tab['icon'] ?>"></i> <?= $tab['title'] ?>
</a>
<?php endforeach; ?>
</div>
<div class="tab-content">
<?php include "tabs/$activeTab.php"; ?>
</div>
</div>
<?php include 'includes/footer.php'; ?>
+47
View File
@@ -0,0 +1,47 @@
# Family Hub
A centralized family organization system with tabs for chores, grocery lists, and meal planning.
## Features
- Tabbed interface for different family needs
- JSON-based data storage
- Daily automated exports
- Mobile-friendly interface
## Setup
1. Clone this repository to your web server
2. Ensure proper permissions on data directory
3. Set up the daily cron job for exports
4. Access the hub at http://your-local-ip/familyHub/
## Directory Structure
familyHub/
├── assets/ # Static assets
│ ├── css/ # CSS files
│ │ └── style.css
│ ├── js/ # JavaScript files
│ │ └── main.js
│ └── img/ # Images
├── config/ # Configuration files
│ └── config.php
├── data/ # JSON data storage (not tracked in git)
│ ├── chores.json
│ ├── groceries.json
│ └── meals.json
├── includes/ # PHP includes/components
│ ├── header.php
│ ├── footer.php
│ ├── db.php # JSON file handling functions
│ └── utils.php # Utility functions
├── exports/ # Temporary location for exports (not tracked in git)
├── scripts/ # Scripts for cron jobs
│ └── daily_export.php
├── tabs/ # Tab-specific functionality
│ ├── chores.php
│ ├── groceries.php
│ └── meals.php
├── .gitignore
├── .cursor.json # Cursor editor configuration
├── README.md
├── index.php # Main entry point
└── export.php # Export functionality
+26
View File
@@ -0,0 +1,26 @@
<?php
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/utils.php';
require_once __DIR__ . '/../config/config.php';
// Export all data types
$types = ['chores', 'groceries', 'meals'];
$results = [];
foreach ($types as $type) {
$data = readJsonFile($type . '.json');
$filename = generateExportFilename($type);
$exportPath = EXPORT_DESTINATION . '/' . $filename;
if (file_put_contents($exportPath, json_encode($data, JSON_PRETTY_PRINT))) {
$results[$type] = 'success';
} else {
$results[$type] = 'failed';
}
}
// Log results
$logFile = EXPORT_DESTINATION . '/export_log.txt';
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[$timestamp] Export results: " . json_encode($results) . "\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
+25
View File
@@ -0,0 +1,25 @@
<?php
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/utils.php';
$chores = readJsonFile('chores.json');
?>
<div id="chores" class="tab-content">
<h2>Chores</h2>
<div class="chores-list">
<?php if (empty($chores)): ?>
<p>No chores added yet.</p>
<?php else: ?>
<ul>
<?php foreach ($chores as $chore): ?>
<li>
<span class="chore-name"><?php echo sanitizeInput($chore['name']); ?></span>
<span class="chore-assignee"><?php echo sanitizeInput($chore['assignee']); ?></span>
<span class="chore-due-date"><?php echo formatDate($chore['due_date']); ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</div>
+25
View File
@@ -0,0 +1,25 @@
<?php
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/utils.php';
$groceries = readJsonFile('groceries.json');
?>
<div id="groceries" class="tab-content">
<h2>Grocery List</h2>
<div class="groceries-list">
<?php if (empty($groceries)): ?>
<p>No items in the grocery list yet.</p>
<?php else: ?>
<ul>
<?php foreach ($groceries as $item): ?>
<li>
<span class="item-name"><?php echo sanitizeInput($item['name']); ?></span>
<span class="item-quantity"><?php echo sanitizeInput($item['quantity']); ?></span>
<span class="item-category"><?php echo sanitizeInput($item['category']); ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</div>
+25
View File
@@ -0,0 +1,25 @@
<?php
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/utils.php';
$meals = readJsonFile('meals.json');
?>
<div id="meals" class="tab-content">
<h2>Meal Planning</h2>
<div class="meals-list">
<?php if (empty($meals)): ?>
<p>No meals planned yet.</p>
<?php else: ?>
<ul>
<?php foreach ($meals as $meal): ?>
<li>
<span class="meal-name"><?php echo sanitizeInput($meal['name']); ?></span>
<span class="meal-date"><?php echo formatDate($meal['date']); ?></span>
<span class="meal-type"><?php echo sanitizeInput($meal['type']); ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</div>