repair realtime conection with realtime firebase + togle button
This commit is contained in:
parent
193d4a04c2
commit
9d3be56527
|
@ -16,3 +16,5 @@ yarn-error.log
|
||||||
/.fleet
|
/.fleet
|
||||||
/.idea
|
/.idea
|
||||||
/.vscode
|
/.vscode
|
||||||
|
!storage/app/*.json
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class ProfileController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('profile', [
|
||||||
|
'user' => Auth::user()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'email' => 'required|email',
|
||||||
|
'current_password' => 'required_with:new_password',
|
||||||
|
'new_password' => 'nullable|min:8|confirmed',
|
||||||
|
'profile_photo' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
$user->email = $request->email;
|
||||||
|
|
||||||
|
// Handle password change
|
||||||
|
if ($request->new_password) {
|
||||||
|
if (!Hash::check($request->current_password, $user->password)) {
|
||||||
|
return back()->withErrors(['current_password' => 'Current password is incorrect']);
|
||||||
|
}
|
||||||
|
$user->password = Hash::make($request->new_password);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle profile photo
|
||||||
|
if ($request->hasFile('profile_photo')) {
|
||||||
|
$photo = $request->file('profile_photo');
|
||||||
|
|
||||||
|
// Replace existing foto.jpg
|
||||||
|
$photo->move(public_path('asset'), 'foto.jpg');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return redirect()->back()->with('success', 'Profile updated successfully');
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ public function register()
|
||||||
{
|
{
|
||||||
$this->app->singleton(FirebaseAuth::class, function ($app) {
|
$this->app->singleton(FirebaseAuth::class, function ($app) {
|
||||||
$factory = (new Factory)
|
$factory = (new Factory)
|
||||||
->withServiceAccount(json_decode(env('FIREBASE_CREDENTIALS'), true))
|
->withServiceAccount(storage_path('app/smartcab-8bb42-firebase-adminsdk-fbsvc-de33a8e45b.json'))
|
||||||
->withDatabaseUri(env('FIREBASE_DATABASE_URL'));
|
->withDatabaseUri(env('FIREBASE_DATABASE_URL'));
|
||||||
|
|
||||||
return $factory->createAuth();
|
return $factory->createAuth();
|
||||||
|
@ -21,8 +21,8 @@ public function register()
|
||||||
|
|
||||||
$this->app->singleton(Database::class, function ($app) {
|
$this->app->singleton(Database::class, function ($app) {
|
||||||
$factory = (new Factory)
|
$factory = (new Factory)
|
||||||
->withServiceAccount(config('firebase.credentials'))
|
->withServiceAccount(storage_path('app/smartcab-8bb42-firebase-adminsdk-fbsvc-de33a8e45b.json'))
|
||||||
->withDatabaseUri(config('firebase.database_url'));
|
->withDatabaseUri(env('FIREBASE_DATABASE_URL'));
|
||||||
|
|
||||||
return $factory->createDatabase();
|
return $factory->createDatabase();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Profile - SmartCab</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100 dark:bg-gray-900">
|
||||||
|
<div class="min-h-screen p-6">
|
||||||
|
<div class="max-w-3xl mx-auto">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex justify-between items-center mb-6">
|
||||||
|
<h1 class="text-2xl font-bold text-gray-800 dark:text-white">Your Profile</h1>
|
||||||
|
<a href="{{ route('welcome') }}" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600">
|
||||||
|
Back to Dashboard
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(session('success'))
|
||||||
|
<div class="mb-4 p-4 bg-green-100 border border-green-400 text-green-700 rounded">
|
||||||
|
{{ session('success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($errors->any())
|
||||||
|
<div class="mb-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded">
|
||||||
|
<ul>
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form action="{{ route('profile.update') }}" method="POST" enctype="multipart/form-data" class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<!-- Profile Photo -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-gray-700 dark:text-gray-200 text-sm font-bold mb-2">Profile Photo</label>
|
||||||
|
<div class="flex items-center space-x-6">
|
||||||
|
<div class="shrink-0">
|
||||||
|
<img id="preview" class="h-32 w-32 object-cover rounded-full" src="{{ asset('asset/foto.jpg') }}" alt="Current profile photo">
|
||||||
|
</div>
|
||||||
|
<label class="block">
|
||||||
|
<span class="sr-only">Choose profile photo</span>
|
||||||
|
<input type="file" name="profile_photo" id="profile_photo"
|
||||||
|
class="block w-full text-sm text-gray-500 dark:text-gray-300
|
||||||
|
file:mr-4 file:py-2 file:px-4
|
||||||
|
file:rounded-full file:border-0
|
||||||
|
file:text-sm file:font-semibold
|
||||||
|
file:bg-blue-50 file:text-blue-700
|
||||||
|
hover:file:bg-blue-100
|
||||||
|
dark:file:bg-gray-700 dark:file:text-white"
|
||||||
|
accept="image/*"
|
||||||
|
onchange="updatePreview(this)"/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="email" class="block text-gray-700 dark:text-gray-200 text-sm font-bold mb-2">Email Address</label>
|
||||||
|
<input type="email" name="email" id="email" value="{{ Auth::user()->email }}"
|
||||||
|
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 dark:text-gray-300 dark:bg-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Password Change Section -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200 mb-4">Change Password</h3>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label for="current_password" class="block text-gray-700 dark:text-gray-200 text-sm font-bold mb-2">Current Password</label>
|
||||||
|
<input type="password" name="current_password" id="current_password"
|
||||||
|
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 dark:text-gray-300 dark:bg-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="new_password" class="block text-gray-700 dark:text-gray-200 text-sm font-bold mb-2">New Password</label>
|
||||||
|
<input type="password" name="new_password" id="new_password"
|
||||||
|
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 dark:text-gray-300 dark:bg-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="new_password_confirmation" class="block text-gray-700 dark:text-gray-200 text-sm font-bold mb-2">Confirm New Password</label>
|
||||||
|
<input type="password" name="new_password_confirmation" id="new_password_confirmation"
|
||||||
|
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 dark:text-gray-300 dark:bg-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submit Button -->
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updatePreview(input) {
|
||||||
|
const file = input.files[0];
|
||||||
|
const preview = document.getElementById('preview');
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
preview.src = e.target.result;
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -301,21 +301,130 @@ class="fixed inset-y-0 left-0 w-full max-w-xs bg-white dark:bg-gray-800 overflow
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Firebase App (the core Firebase SDK) -->
|
<!-- Firebase SDK v8 -->
|
||||||
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
|
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
|
||||||
<!-- Firebase Database -->
|
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js"></script>
|
||||||
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Your web app's Firebase configuration
|
// Firebase configuration
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyCr8xNQIsPpIUMGIR9wEGmG7hgMDKf2H5I",
|
||||||
|
authDomain: "smartcab-8bb42.firebaseapp.com",
|
||||||
databaseURL: "https://smartcab-8bb42-default-rtdb.firebaseio.com",
|
databaseURL: "https://smartcab-8bb42-default-rtdb.firebaseio.com",
|
||||||
|
projectId: "smartcab-8bb42",
|
||||||
|
storageBucket: "smartcab-8bb42.firebasestorage.app",
|
||||||
|
messagingSenderId: "539751617121",
|
||||||
|
appId: "1:539751617121:web:3a899309fdb5e29efa9020",
|
||||||
|
measurementId: "G-BQPQLLCJTR"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize Firebase
|
// Initialize Firebase
|
||||||
firebase.initializeApp(firebaseConfig);
|
firebase.initializeApp(firebaseConfig);
|
||||||
const database = firebase.database();
|
const database = firebase.database();
|
||||||
|
|
||||||
|
let isUserAction = false;
|
||||||
|
|
||||||
|
// Function to update UI elements
|
||||||
|
function updateUIElement(elementId, value, suffix = '') {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
if (element) {
|
||||||
|
element.textContent = `${value}${suffix}`;
|
||||||
|
console.log(`Updated ${elementId} with value: ${value}${suffix}`);
|
||||||
|
} else {
|
||||||
|
console.error(`Element ${elementId} not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update UI with new data
|
||||||
|
function updateUI(data) {
|
||||||
|
console.log('Updating UI with data:', data);
|
||||||
|
|
||||||
|
if (data.dht11) {
|
||||||
|
updateUIElement('temp-value', data.dht11.temperature, '°C');
|
||||||
|
updateUIElement('humidity-value', data.dht11.humidity, '%');
|
||||||
|
|
||||||
|
// Update chart if it exists
|
||||||
|
if (typeof sensorChart !== 'undefined') {
|
||||||
|
updateChart(Date.now(), data.dht11.temperature, data.dht11.humidity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.security) {
|
||||||
|
updateUIElement('motion-value', data.security.motion);
|
||||||
|
updateUIElement('status-value', data.security.status);
|
||||||
|
|
||||||
|
const toggle = document.getElementById('securityToggle');
|
||||||
|
if (toggle) {
|
||||||
|
toggle.checked = data.security.status === 'on';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to fetch and update data
|
||||||
|
function fetchAndUpdateData() {
|
||||||
|
fetch('https://smartcab-8bb42-default-rtdb.firebaseio.com/.json')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Data fetched successfully:', data);
|
||||||
|
updateUI(data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fetch error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to toggle security status
|
||||||
|
function toggleSecurity(checkbox) {
|
||||||
|
isUserAction = true;
|
||||||
|
console.log('Toggle security called with state:', checkbox.checked);
|
||||||
|
|
||||||
|
const securityRef = database.ref('security/status');
|
||||||
|
const newStatus = checkbox.checked ? 'on' : 'off';
|
||||||
|
|
||||||
|
checkbox.disabled = true;
|
||||||
|
|
||||||
|
securityRef.set(newStatus)
|
||||||
|
.then(() => {
|
||||||
|
console.log('Security status updated successfully to:', newStatus);
|
||||||
|
updateUIElement('status-value', newStatus);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error updating security status:', error);
|
||||||
|
checkbox.checked = !checkbox.checked;
|
||||||
|
alert('Failed to update security status. Please try again.');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
checkbox.disabled = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
isUserAction = false;
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for security status changes
|
||||||
|
database.ref('security/status').on('value', (snapshot) => {
|
||||||
|
const status = snapshot.val();
|
||||||
|
if (status && !isUserAction) {
|
||||||
|
console.log('Security status updated:', status);
|
||||||
|
updateUIElement('status-value', status);
|
||||||
|
|
||||||
|
const securityToggle = document.getElementById('securityToggle');
|
||||||
|
if (securityToggle && !securityToggle.disabled) {
|
||||||
|
securityToggle.checked = status === 'on';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize when document is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
console.log('Document ready, initializing...');
|
||||||
|
fetchAndUpdateData();
|
||||||
|
setInterval(fetchAndUpdateData, 1000);
|
||||||
|
|
||||||
|
// Initialize charts
|
||||||
|
initializeCharts();
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize the chart with empty data
|
// Initialize the chart with empty data
|
||||||
const ctx = document.getElementById('sensorChart').getContext('2d');
|
const ctx = document.getElementById('sensorChart').getContext('2d');
|
||||||
const sensorChart = new Chart(ctx, {
|
const sensorChart = new Chart(ctx, {
|
||||||
|
@ -394,13 +503,6 @@ function updateChart(timestamp, temperature, humidity) {
|
||||||
sensorChart.update();
|
sensorChart.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for real-time updates
|
|
||||||
database.ref('dht11').on('value', (snapshot) => {
|
|
||||||
const data = snapshot.val();
|
|
||||||
const timestamp = Date.now();
|
|
||||||
updateChart(timestamp, data.temperature, data.humidity);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mobile Menu Toggle
|
// Mobile Menu Toggle
|
||||||
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
|
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
|
||||||
const mobileMenu = document.getElementById('mobileMenu');
|
const mobileMenu = document.getElementById('mobileMenu');
|
||||||
|
@ -556,29 +658,6 @@ function initializeCharts() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeCharts();
|
|
||||||
|
|
||||||
// Real-time listeners for sensor data
|
|
||||||
const dbRef = firebase.database();
|
|
||||||
|
|
||||||
// DHT11 Sensor listener
|
|
||||||
dbRef.ref('dht11').on('value', (snapshot) => {
|
|
||||||
const data = snapshot.val();
|
|
||||||
if (data) {
|
|
||||||
document.getElementById('temp-value').textContent = `${data.temperature}°C`;
|
|
||||||
document.getElementById('humidity-value').textContent = `${data.humidity}%`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Security listener
|
|
||||||
dbRef.ref('security').on('value', (snapshot) => {
|
|
||||||
const data = snapshot.val();
|
|
||||||
if (data) {
|
|
||||||
document.getElementById('motion-value').textContent = data.motion;
|
|
||||||
document.getElementById('status-value').textContent = data.status;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add these functions to your existing script
|
// Add these functions to your existing script
|
||||||
function toggleModal() {
|
function toggleModal() {
|
||||||
const modal = document.getElementById('controlModal');
|
const modal = document.getElementById('controlModal');
|
||||||
|
@ -586,26 +665,12 @@ function toggleModal() {
|
||||||
|
|
||||||
// Update toggle state when modal opens
|
// Update toggle state when modal opens
|
||||||
if (!modal.classList.contains('hidden')) {
|
if (!modal.classList.contains('hidden')) {
|
||||||
dbRef.ref('security/status').once('value', (snapshot) => {
|
database.ref('security/status').once('value', (snapshot) => {
|
||||||
const status = snapshot.val();
|
const status = snapshot.val();
|
||||||
document.getElementById('securityToggle').checked = status === 'on';
|
document.getElementById('securityToggle').checked = status === 'on';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSecurity(checkbox) {
|
|
||||||
const newStatus = checkbox.checked ? 'on' : 'off';
|
|
||||||
dbRef.ref('security').update({
|
|
||||||
status: newStatus
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for security status changes
|
|
||||||
dbRef.ref('security/status').on('value', (snapshot) => {
|
|
||||||
const status = snapshot.val();
|
|
||||||
document.getElementById('securityToggle').checked = status === 'on';
|
|
||||||
document.getElementById('status-value').textContent = status;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
use App\Http\Controllers\Auth\FirebaseAuthController;
|
use App\Http\Controllers\Auth\FirebaseAuthController;
|
||||||
use App\Http\Controllers\AIChatController;
|
use App\Http\Controllers\AIChatController;
|
||||||
use App\Http\Controllers\WelcomeController;
|
use App\Http\Controllers\WelcomeController;
|
||||||
|
use App\Http\Controllers\ProfileController;
|
||||||
|
|
||||||
Route::get('/login', [FirebaseAuthController::class, 'showLogin'])
|
Route::get('/login', [FirebaseAuthController::class, 'showLogin'])
|
||||||
->name('login')
|
->name('login')
|
||||||
|
@ -30,3 +31,6 @@
|
||||||
}
|
}
|
||||||
return view('auth.login');
|
return view('auth.login');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
|
||||||
|
Route::post('/profile/update', [ProfileController::class, 'update'])->name('profile.update');
|
||||||
|
|
Loading…
Reference in New Issue