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

This commit is contained in:
LouisWhit 2025-05-10 17:12:58 -05:00
commit 1a65e69d25
21 changed files with 572 additions and 0 deletions

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"

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
.gitignore vendored Normal file
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
.htaccess Normal file
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
assets/css/style.css Normal file
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
assets/js/main.js Normal file
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
config/config.php Normal file
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
config/env.php Normal file
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
env.example Normal file
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
export.php Normal file
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
gitignore Normal file
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
includes/db.php Normal file
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
includes/footer.php Normal file
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/bootstrap@5.3.0/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
includes/header.php Normal file
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/bootstrap@5.3.0/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
includes/utils.php Normal file
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
index.php Normal file
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
readme.md Normal file
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
scripts/daily_export.php Normal file
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
tabs/chores.php Normal file
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
tabs/groceries.php Normal file
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
tabs/meals.php Normal file
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>