first commit
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
40
.idea/appInsightsSettings.xml
generated
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AppInsightsSettings">
|
||||||
|
<option name="tabSettings">
|
||||||
|
<map>
|
||||||
|
<entry key="Android Vitals">
|
||||||
|
<value>
|
||||||
|
<InsightsFilterSettings>
|
||||||
|
<option name="failureTypes">
|
||||||
|
<list>
|
||||||
|
<option value="ANR" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="signal" value="SIGNAL_UNSPECIFIED" />
|
||||||
|
<option name="timeIntervalDays" value="SEVEN_DAYS" />
|
||||||
|
<option name="visibilityType" value="ALL" />
|
||||||
|
</InsightsFilterSettings>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="Firebase Crashlytics">
|
||||||
|
<value>
|
||||||
|
<InsightsFilterSettings>
|
||||||
|
<option name="connection">
|
||||||
|
<ConnectionSetting>
|
||||||
|
<option name="appId" value="com.example.pap_teste" />
|
||||||
|
<option name="mobileSdkAppId" value="1:476421715902:android:4147ab5f1cde601e1aebef" />
|
||||||
|
<option name="projectId" value="namesa-429c1" />
|
||||||
|
<option name="projectNumber" value="476421715902" />
|
||||||
|
</ConnectionSetting>
|
||||||
|
</option>
|
||||||
|
<option name="signal" value="SIGNAL_UNSPECIFIED" />
|
||||||
|
<option name="timeIntervalDays" value="THIRTY_DAYS" />
|
||||||
|
<option name="visibilityType" value="ALL" />
|
||||||
|
</InsightsFilterSettings>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
18
.idea/deploymentTargetSelector.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2026-04-14T16:06:26.670067Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=b93659d0e5dd" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
13
.idea/deviceManager.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DeviceTable">
|
||||||
|
<option name="columnSorters">
|
||||||
|
<list>
|
||||||
|
<ColumnSorterState>
|
||||||
|
<option name="column" value="Name" />
|
||||||
|
<option name="order" value="ASCENDING" />
|
||||||
|
</ColumnSorterState>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
19
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/markdown.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MarkdownSettings">
|
||||||
|
<option name="previewPanelProviderInfo">
|
||||||
|
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/migrations.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
17
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/studiobot.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="StudioBotProjectSettings">
|
||||||
|
<option name="shareContext" value="OptedIn" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
|
}
|
||||||
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
51
app/build.gradle.kts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.google.services)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.example.pap_teste"
|
||||||
|
compileSdk = 36
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.example.pap_teste"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 36
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation(libs.appcompat)
|
||||||
|
implementation(libs.material)
|
||||||
|
implementation(libs.activity)
|
||||||
|
implementation(libs.constraintlayout)
|
||||||
|
implementation(platform(libs.firebase.bom))
|
||||||
|
implementation("com.google.firebase:firebase-firestore")
|
||||||
|
implementation("com.google.firebase:firebase-auth")
|
||||||
|
implementation(libs.firebase.database)
|
||||||
|
implementation(libs.glide)
|
||||||
|
implementation(libs.firebase.storage)
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(libs.ext.junit)
|
||||||
|
androidTestImplementation(libs.espresso.core)
|
||||||
|
androidTestImplementation(libs.test.core)
|
||||||
|
}
|
||||||
29
app/google-services.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "476421715902",
|
||||||
|
"project_id": "namesa-429c1",
|
||||||
|
"storage_bucket": "namesa-429c1.firebasestorage.app"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:476421715902:android:4147ab5f1cde601e1aebef",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.example.pap_teste"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyCPz7Pd3tJj3QkF7fV_vudCJythNsyR57k"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
21
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
assertEquals("com.example.pap_teste", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
118
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
|
|
||||||
|
<!-- Bluetooth permissions for energy/power stats and location accuracy -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@drawable/na_mesa"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@drawable/na_mesa"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Pap_teste">
|
||||||
|
<activity
|
||||||
|
android:name=".AddStaffActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".DetalhesReservasActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Detalhes das reservas"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".GerirMesasActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Editar mesas"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".EstablishmentDashboardActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Área do Estabelecimento"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".ListaEsperaActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Lista de espera"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".GestaoStaffActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Gestão de staff"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ClientDashboardActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Área do Cliente"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".NovaReservaActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Nova reserva"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".ExplorarRestaurantesActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Explorar restaurantes"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".FavoritosActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Favoritos"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".CheckInAntecipadoActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Check-in antecipado"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".PartilharReservaActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Partilhar reserva"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".AccountCreatedActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Conta criada"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".DefinicoesAdminActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Definições"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".ProfileDashboardActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="O meu Perfil"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".MinhasReservasActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="Minhas Reservas"
|
||||||
|
android:theme="@style/Theme.Pap_teste" />
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tela de confirmação apresentada logo após a criação de conta.
|
||||||
|
* Exibe os dados básicos e direciona para a área correspondente
|
||||||
|
* (estabelecimento como admin, cliente como cliente).
|
||||||
|
*/
|
||||||
|
public class AccountCreatedActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_account_created);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.accountCreatedRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
TextView txtWelcome = findViewById(R.id.txtWelcome);
|
||||||
|
TextView txtEmail = findViewById(R.id.txtEmail);
|
||||||
|
TextView txtRole = findViewById(R.id.txtRole);
|
||||||
|
Button btnContinue = findViewById(R.id.btnContinuar);
|
||||||
|
Button btnBack = findViewById(R.id.btnVoltar);
|
||||||
|
|
||||||
|
String displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
||||||
|
String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
String accountType = getIntent().getStringExtra(MainActivity.EXTRA_ACCOUNT_TYPE);
|
||||||
|
String role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE);
|
||||||
|
|
||||||
|
txtWelcome.setText(displayName != null ? displayName : "Novo utilizador");
|
||||||
|
txtEmail.setText(email != null ? email : "Email não informado");
|
||||||
|
txtRole.setText(role != null ? role : "CLIENTE");
|
||||||
|
|
||||||
|
btnContinue.setOnClickListener(v -> {
|
||||||
|
boolean isEstablishment = "ESTABELECIMENTO".equalsIgnoreCase(accountType);
|
||||||
|
Intent nextScreen = new Intent(
|
||||||
|
this,
|
||||||
|
isEstablishment ? EstablishmentDashboardActivity.class : ClientDashboardActivity.class
|
||||||
|
);
|
||||||
|
nextScreen.putExtra(MainActivity.EXTRA_ACTION_MODE, MainActivity.AccountAction.CRIAR.name());
|
||||||
|
nextScreen.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName);
|
||||||
|
nextScreen.putExtra(MainActivity.EXTRA_EMAIL, email);
|
||||||
|
nextScreen.putExtra(MainActivity.EXTRA_ACCOUNT_TYPE, accountType);
|
||||||
|
nextScreen.putExtra(MainActivity.EXTRA_ROLE, role);
|
||||||
|
startActivity(nextScreen);
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (btnBack != null) {
|
||||||
|
btnBack.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
135
app/src/main/java/com/example/pap_teste/AddStaffActivity.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.text.InputType;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Staff;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
|
||||||
|
public class AddStaffActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private Button addButton;
|
||||||
|
private EditText nameEditText;
|
||||||
|
private Spinner zonaSpinner;
|
||||||
|
private Button btnAddZone;
|
||||||
|
private Spinner mesaSpinner;
|
||||||
|
private ArrayList<String> zones;
|
||||||
|
private ArrayList<String> mesas;
|
||||||
|
private ArrayAdapter<String> adapter;
|
||||||
|
private ArrayAdapter<String> mesaAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_add_staff);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
addButton = findViewById(R.id.addButton);
|
||||||
|
nameEditText = findViewById(R.id.nammeEditText);
|
||||||
|
zonaSpinner = findViewById(R.id.zonaSpinner);
|
||||||
|
btnAddZone = findViewById(R.id.btnAddZone);
|
||||||
|
mesaSpinner = findViewById(R.id.mesaSpinner);
|
||||||
|
|
||||||
|
Button btnVoltarAddStaff = findViewById(R.id.btnVoltarAddStaff);
|
||||||
|
if (btnVoltarAddStaff != null) {
|
||||||
|
btnVoltarAddStaff.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
zones = new ArrayList<>();
|
||||||
|
zones.add("Sala");
|
||||||
|
zones.add("Esplanada");
|
||||||
|
zones.add("Balcão");
|
||||||
|
|
||||||
|
adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, zones);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
zonaSpinner.setAdapter(adapter);
|
||||||
|
|
||||||
|
mesas = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= 10; i++) {
|
||||||
|
mesas.add("Mesa " + i);
|
||||||
|
}
|
||||||
|
mesaAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, mesas);
|
||||||
|
mesaAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
mesaSpinner.setAdapter(mesaAdapter);
|
||||||
|
|
||||||
|
btnAddZone.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(AddStaffActivity.this);
|
||||||
|
builder.setTitle("Adicionar Zona");
|
||||||
|
|
||||||
|
final EditText input = new EditText(AddStaffActivity.this);
|
||||||
|
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
|
builder.setView(input);
|
||||||
|
|
||||||
|
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
String newZone = input.getText().toString();
|
||||||
|
if (!newZone.isEmpty()) {
|
||||||
|
zones.add(newZone);
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
zonaSpinner.setSelection(zones.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
String name = nameEditText.getText().toString();
|
||||||
|
String zona = "";
|
||||||
|
if (zonaSpinner.getSelectedItem() != null) {
|
||||||
|
zona = zonaSpinner.getSelectedItem().toString();
|
||||||
|
}
|
||||||
|
String mesa = "";
|
||||||
|
if (mesaSpinner.getSelectedItem() != null) {
|
||||||
|
mesa = mesaSpinner.getSelectedItem().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("Staff");
|
||||||
|
String uuid = java.util.UUID.randomUUID().toString();
|
||||||
|
Staff staff = new Staff(name, zona, mesa, uuid);
|
||||||
|
databaseReference.child(uuid).setValue(staff);
|
||||||
|
|
||||||
|
Toast.makeText(AddStaffActivity.this, "Staff adicionado com sucesso!", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(AddStaffActivity.this, "Erro: Preencha todos os campos", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationListener;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CheckInAntecipadoActivity extends AppCompatActivity implements LocationListener {
|
||||||
|
|
||||||
|
private TextView txtDistancia, txtStatus, txtEndereco;
|
||||||
|
private Button btnConfirmarChegada, btnAbrirMapa;
|
||||||
|
private LocationManager locationManager;
|
||||||
|
private DatabaseReference databaseReference;
|
||||||
|
|
||||||
|
private double restaurantLat, restaurantLon;
|
||||||
|
private int securityDistance = 500; // default 500m
|
||||||
|
private boolean settingsLoaded = false;
|
||||||
|
private String restaurantAddress;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_checkin_antecipado);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.checkinRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
txtDistancia = findViewById(R.id.txtDistancia);
|
||||||
|
txtStatus = findViewById(R.id.txtStatusDistancia);
|
||||||
|
txtEndereco = findViewById(R.id.txtEnderecoRestaurante);
|
||||||
|
btnConfirmarChegada = findViewById(R.id.btnConfirmarChegada);
|
||||||
|
btnAbrirMapa = findViewById(R.id.btnAbrirMapa);
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
String restaurantEmail = getIntent().getStringExtra("restaurant_email");
|
||||||
|
if (restaurantEmail != null) {
|
||||||
|
String restaurantId = restaurantEmail.replace(".", "_").replace("@", "_at_");
|
||||||
|
loadRestaurantSettings(restaurantId);
|
||||||
|
} else {
|
||||||
|
txtStatus.setText("Erro: Restaurante não identificado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
if (hasRequiredPermissions()) {
|
||||||
|
startLocationUpdates();
|
||||||
|
} else {
|
||||||
|
txtStatus.setText("Permissões em falta para localização/proximidade.");
|
||||||
|
}
|
||||||
|
|
||||||
|
btnConfirmarChegada.setOnClickListener(v -> {
|
||||||
|
Toast.makeText(this, "Chegada confirmada com sucesso!", Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
btnAbrirMapa.setOnClickListener(v -> {
|
||||||
|
if (restaurantAddress != null) {
|
||||||
|
try {
|
||||||
|
String uri = "geo:0,0?q=" + java.net.URLEncoder.encode(restaurantAddress, "UTF-8");
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, android.net.Uri.parse(uri));
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, "Erro ao abrir mapa.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRequiredPermissions() {
|
||||||
|
boolean fineLocation = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
boolean btScan = ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED;
|
||||||
|
return fineLocation && btScan;
|
||||||
|
}
|
||||||
|
return fineLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRestaurantSettings(String restaurantId) {
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference().child("users").child(restaurantId);
|
||||||
|
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
if (snapshot.exists()) {
|
||||||
|
restaurantAddress = snapshot.child("address").getValue(String.class);
|
||||||
|
Integer dist = snapshot.child("securityDistance").getValue(Integer.class);
|
||||||
|
securityDistance = dist != null ? dist : 500;
|
||||||
|
|
||||||
|
if (restaurantAddress != null) {
|
||||||
|
txtEndereco.setText("Morada: " + restaurantAddress);
|
||||||
|
geocodeAddress(restaurantAddress);
|
||||||
|
} else {
|
||||||
|
txtEndereco.setText("Morada não disponível.");
|
||||||
|
// Fallback to old lat/lon if they exist
|
||||||
|
Double lat = snapshot.child("latitude").getValue(Double.class);
|
||||||
|
Double lon = snapshot.child("longitude").getValue(Double.class);
|
||||||
|
if (lat != null && lon != null) {
|
||||||
|
restaurantLat = lat;
|
||||||
|
restaurantLon = lon;
|
||||||
|
settingsLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
txtStatus.setText("Aviso: Definições do restaurante não encontradas.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
txtStatus.setText("Erro ao carregar definições do restaurante.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void geocodeAddress(String address) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
android.location.Geocoder geocoder = new android.location.Geocoder(this);
|
||||||
|
List<android.location.Address> addresses = geocoder.getFromLocationName(address, 1);
|
||||||
|
if (addresses != null && !addresses.isEmpty()) {
|
||||||
|
restaurantLat = addresses.get(0).getLatitude();
|
||||||
|
restaurantLon = addresses.get(0).getLongitude();
|
||||||
|
settingsLoaded = true;
|
||||||
|
} else {
|
||||||
|
runOnUiThread(() -> txtStatus.setText("Erro: Não foi possível localizar a morada no mapa."));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
runOnUiThread(() -> txtStatus.setText("Erro ao obter coordenadas da morada."));
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLocationUpdates() {
|
||||||
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 5, this);
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 5, this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
txtStatus.setText("Erro ao iniciar localização: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(@NonNull Location location) {
|
||||||
|
if (!settingsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float[] results = new float[1];
|
||||||
|
Location.distanceBetween(location.getLatitude(), location.getLongitude(), restaurantLat, restaurantLon,
|
||||||
|
results);
|
||||||
|
float distanceInMeters = results[0];
|
||||||
|
|
||||||
|
txtDistancia.setText(String.format("Distância: %.0f metros", distanceInMeters));
|
||||||
|
|
||||||
|
if (distanceInMeters <= securityDistance) {
|
||||||
|
txtStatus.setText("Está no raio de segurança. Pode confirmar a sua chegada.");
|
||||||
|
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
|
||||||
|
btnConfirmarChegada.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
txtStatus.setText(String.format("Está fora do raio de segurança (%dm). Aproxime-se para fazer check-in.",
|
||||||
|
securityDistance));
|
||||||
|
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
|
||||||
|
btnConfirmarChegada.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (locationManager != null) {
|
||||||
|
locationManager.removeUpdates(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderEnabled(@NonNull String provider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderDisabled(@NonNull String provider) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.pap_teste.models.FoodCategory;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ClientDashboardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private String email, displayName, role;
|
||||||
|
private TextView txtGreeting;
|
||||||
|
private androidx.activity.result.ActivityResultLauncher<Intent> profileLauncher;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_client_dashboard);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.clientRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
txtGreeting = findViewById(R.id.txtClientGreeting);
|
||||||
|
|
||||||
|
email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
||||||
|
role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE);
|
||||||
|
|
||||||
|
profileLauncher = registerForActivityResult(
|
||||||
|
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
||||||
|
String updatedName = result.getData()
|
||||||
|
.getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
||||||
|
if (updatedName != null) {
|
||||||
|
displayName = updatedName;
|
||||||
|
updateGreeting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateGreeting();
|
||||||
|
fetchProfilePicture();
|
||||||
|
setupCategories();
|
||||||
|
setupActions();
|
||||||
|
loadNextReservation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGreeting() {
|
||||||
|
txtGreeting.setText(String.format("Olá, %s", displayName != null ? displayName : "convidado"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchProfilePicture() {
|
||||||
|
if (email == null) return;
|
||||||
|
String documentId = email.replace(".", "_").replace("@", "_at_");
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference()
|
||||||
|
.child("users").child(documentId).child("photoUrl")
|
||||||
|
.get().addOnSuccessListener(snapshot -> {
|
||||||
|
if (!isDestroyed() && snapshot.exists()) {
|
||||||
|
String photoUrl = snapshot.getValue(String.class);
|
||||||
|
if (photoUrl != null && !photoUrl.isEmpty()) {
|
||||||
|
android.widget.ImageView imgProfile = findViewById(R.id.imgProfile);
|
||||||
|
com.bumptech.glide.Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCategories() {
|
||||||
|
RecyclerView rv = findViewById(R.id.rvCategories);
|
||||||
|
List<FoodCategory> cats = new ArrayList<>();
|
||||||
|
cats.add(new FoodCategory("Carnes", R.drawable.cat_carnes));
|
||||||
|
cats.add(new FoodCategory("Massas", R.drawable.cat_massas));
|
||||||
|
cats.add(new FoodCategory("Sushi", R.drawable.cat_sushi));
|
||||||
|
cats.add(new FoodCategory("Pizzas", R.drawable.cat_pizzas));
|
||||||
|
cats.add(new FoodCategory("Sobremesas", R.drawable.cat_sobremesas));
|
||||||
|
|
||||||
|
FoodCategoryAdapter adapter = new FoodCategoryAdapter(cats, category -> {
|
||||||
|
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
||||||
|
intent.putExtra("category_filter", category.getName());
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
rv.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActions() {
|
||||||
|
findViewById(R.id.cardProfile).setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(this, ProfileDashboardActivity.class);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_EMAIL, email);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName);
|
||||||
|
profileLauncher.launch(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
findViewById(R.id.btnPartilhar).setOnClickListener(
|
||||||
|
v -> startActivity(new Intent(this, PartilharReservaActivity.class)));
|
||||||
|
|
||||||
|
findViewById(R.id.btnNovaReserva)
|
||||||
|
.setOnClickListener(v -> startActivity(new Intent(this, NovaReservaActivity.class)));
|
||||||
|
|
||||||
|
findViewById(R.id.btnMinhasReservas)
|
||||||
|
.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(this, MinhasReservasActivity.class);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_EMAIL, email);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadNextReservation() {
|
||||||
|
if (email == null) return;
|
||||||
|
com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
ref.orderByChild("clienteEmail").equalTo(email).addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
com.example.pap_teste.models.Reserva proxima = null;
|
||||||
|
long timeProx = Long.MAX_VALUE;
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm", java.util.Locale.getDefault());
|
||||||
|
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Reserva r = ds.getValue(com.example.pap_teste.models.Reserva.class);
|
||||||
|
if (r != null && (r.getEstado().startsWith("Confirmada") || r.getEstado().equals("Pendente"))) {
|
||||||
|
try {
|
||||||
|
java.util.Date rDate = sdf.parse(r.getData() + " " + r.getHora());
|
||||||
|
if (rDate != null) {
|
||||||
|
long rTime = rDate.getTime();
|
||||||
|
// Consider reservations from the last 2 hours and in the future
|
||||||
|
if (rTime >= currentTime - 2L * 60 * 60 * 1000) {
|
||||||
|
if (rTime < timeProx) {
|
||||||
|
timeProx = rTime;
|
||||||
|
proxima = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView txtTitle = findViewById(R.id.txtResTitle);
|
||||||
|
TextView txtTime = findViewById(R.id.txtResTime);
|
||||||
|
Button btnCheckIn = findViewById(R.id.btnCheckIn);
|
||||||
|
Button btnPartilhar = findViewById(R.id.btnPartilhar);
|
||||||
|
|
||||||
|
if (proxima != null) {
|
||||||
|
txtTitle.setText(proxima.getRestauranteName());
|
||||||
|
|
||||||
|
// Formatting to show "Hoje", "Amanhã" or the date itself
|
||||||
|
String dateStr = proxima.getData();
|
||||||
|
try {
|
||||||
|
java.util.Date rDate = sdf.parse(proxima.getData() + " " + proxima.getHora());
|
||||||
|
if (rDate != null) {
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
cal.setTime(rDate);
|
||||||
|
|
||||||
|
java.util.Calendar today = java.util.Calendar.getInstance();
|
||||||
|
|
||||||
|
java.util.Calendar tmr = java.util.Calendar.getInstance();
|
||||||
|
tmr.add(java.util.Calendar.DAY_OF_YEAR, 1);
|
||||||
|
|
||||||
|
if (cal.get(java.util.Calendar.YEAR) == today.get(java.util.Calendar.YEAR) && cal.get(java.util.Calendar.DAY_OF_YEAR) == today.get(java.util.Calendar.DAY_OF_YEAR)) {
|
||||||
|
dateStr = "Hoje";
|
||||||
|
} else if (cal.get(java.util.Calendar.YEAR) == tmr.get(java.util.Calendar.YEAR) && cal.get(java.util.Calendar.DAY_OF_YEAR) == tmr.get(java.util.Calendar.DAY_OF_YEAR)) {
|
||||||
|
dateStr = "Amanhã";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
txtTime.setText(String.format("%s às %s • %d pessoas", dateStr, proxima.getHora(), proxima.getPessoas()));
|
||||||
|
btnCheckIn.setVisibility(android.view.View.VISIBLE);
|
||||||
|
btnPartilhar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
|
||||||
|
String encodedEmail = proxima.getRestauranteEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users").child(encodedEmail)
|
||||||
|
.child("logoUrl").get().addOnSuccessListener(s -> {
|
||||||
|
if (!isDestroyed() && s.exists() && s.getValue() != null) {
|
||||||
|
android.widget.ImageView imgResIcon = findViewById(R.id.imgResIcon);
|
||||||
|
com.bumptech.glide.Glide.with(ClientDashboardActivity.this).load(s.getValue(String.class)).circleCrop().into(imgResIcon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
com.example.pap_teste.models.Reserva finalProxima = proxima;
|
||||||
|
btnCheckIn.setOnClickListener(v -> {
|
||||||
|
boolean hasLocationPermission = androidx.core.app.ActivityCompat.checkSelfPermission(ClientDashboardActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
if (!hasLocationPermission) {
|
||||||
|
android.widget.Toast.makeText(ClientDashboardActivity.this, "Dê permissões de localização para aceder ao check-in.", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(ClientDashboardActivity.this, CheckInAntecipadoActivity.class);
|
||||||
|
intent.putExtra("restaurant_email", finalProxima.getRestauranteEmail());
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
txtTitle.setText("Sem reservas ativas");
|
||||||
|
txtTime.setText("Explore os restaurantes e reserve!");
|
||||||
|
btnCheckIn.setVisibility(android.view.View.GONE);
|
||||||
|
btnPartilhar.setVisibility(android.view.View.GONE);
|
||||||
|
android.widget.ImageView imgResIcon = findViewById(R.id.imgResIcon);
|
||||||
|
imgResIcon.setImageResource(R.drawable.circle_bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onCancelled(com.google.firebase.database.DatabaseError error) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.firebase.storage.FirebaseStorage;
|
||||||
|
import com.google.firebase.storage.StorageReference;
|
||||||
|
import java.util.UUID;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
|
||||||
|
public class DefinicoesAdminActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private EditText inputRadius, inputAddress;
|
||||||
|
private android.widget.Spinner spinnerCategory;
|
||||||
|
private ImageView imgLogo;
|
||||||
|
private DatabaseReference databaseReference;
|
||||||
|
private String documentId;
|
||||||
|
private String photoUrl;
|
||||||
|
private ActivityResultLauncher<Intent> imagePickerLauncher;
|
||||||
|
private String[] categories = {"Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas", "Italiana", "Moderna"};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_definicoes_admin);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.definicoesRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (email != null) {
|
||||||
|
documentId = email.replace(".", "_").replace("@", "_at_");
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference().child("users");
|
||||||
|
|
||||||
|
inputRadius = findViewById(R.id.inputRadius);
|
||||||
|
inputAddress = findViewById(R.id.inputAddress);
|
||||||
|
spinnerCategory = findViewById(R.id.spinnerCategory);
|
||||||
|
imgLogo = findViewById(R.id.imgRestaurantLogo);
|
||||||
|
|
||||||
|
android.widget.ArrayAdapter<String> adapter = new android.widget.ArrayAdapter<>(this,
|
||||||
|
android.R.layout.simple_spinner_item, categories);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerCategory.setAdapter(adapter);
|
||||||
|
|
||||||
|
Button btnSave = findViewById(R.id.btnSaveSettings);
|
||||||
|
Button btnBack = findViewById(R.id.btnVoltar);
|
||||||
|
Button btnChangeLogo = findViewById(R.id.btnChangeLogo);
|
||||||
|
|
||||||
|
if (btnBack != null) {
|
||||||
|
btnBack.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
imagePickerLauncher = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
||||||
|
Uri imageUri = result.getData().getData();
|
||||||
|
if (imageUri != null) {
|
||||||
|
uploadImageToFirebase(imageUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnChangeLogo.setOnClickListener(v -> {
|
||||||
|
String[] options = {"Galeria", "URL da Imagem"};
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Escolher Logótipo")
|
||||||
|
.setItems(options, (dialog, which) -> {
|
||||||
|
if (which == 0) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK);
|
||||||
|
intent.setType("image/*");
|
||||||
|
imagePickerLauncher.launch(intent);
|
||||||
|
} else {
|
||||||
|
showUrlInputDialog();
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
loadCurrentSettings();
|
||||||
|
|
||||||
|
btnSave.setOnClickListener(v -> saveSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showUrlInputDialog() {
|
||||||
|
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
|
||||||
|
builder.setTitle("Inserir URL da Imagem");
|
||||||
|
|
||||||
|
final EditText input = new EditText(this);
|
||||||
|
input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_URI);
|
||||||
|
builder.setView(input);
|
||||||
|
|
||||||
|
builder.setPositiveButton("Confirmar", (dialog, which) -> {
|
||||||
|
String url = input.getText().toString().trim();
|
||||||
|
if (!TextUtils.isEmpty(url)) {
|
||||||
|
this.photoUrl = url;
|
||||||
|
Glide.with(this).load(photoUrl).circleCrop().into(imgLogo);
|
||||||
|
Toast.makeText(this, "Logótipo definido!", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("Cancelar", (dialog, which) -> dialog.cancel());
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadImageToFirebase(Uri imageUri) {
|
||||||
|
if (documentId == null) return;
|
||||||
|
|
||||||
|
StorageReference storageRef = FirebaseStorage.getInstance().getReference()
|
||||||
|
.child("restaurant_logos/" + UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
||||||
|
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
||||||
|
photoUrl = uri.toString();
|
||||||
|
Glide.with(this).load(photoUrl).circleCrop().into(imgLogo);
|
||||||
|
});
|
||||||
|
}).addOnFailureListener(e -> {
|
||||||
|
Toast.makeText(this, "Falha no upload: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCurrentSettings() {
|
||||||
|
if (documentId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
databaseReference.child(documentId).addListenerForSingleValueEvent(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
if (snapshot.exists()) {
|
||||||
|
if (snapshot.hasChild("securityDistance")) {
|
||||||
|
Object val = snapshot.child("securityDistance").getValue();
|
||||||
|
inputRadius.setText(val != null ? val.toString() : "");
|
||||||
|
}
|
||||||
|
if (snapshot.hasChild("address")) {
|
||||||
|
String addr = snapshot.child("address").getValue(String.class);
|
||||||
|
inputAddress.setText(addr != null ? addr : "");
|
||||||
|
}
|
||||||
|
if (snapshot.hasChild("category")) {
|
||||||
|
String cat = snapshot.child("category").getValue(String.class);
|
||||||
|
if (cat != null) {
|
||||||
|
for (int i = 0; i < categories.length; i++) {
|
||||||
|
if (categories[i].equalsIgnoreCase(cat)) {
|
||||||
|
spinnerCategory.setSelection(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (snapshot.hasChild("logoUrl")) {
|
||||||
|
photoUrl = snapshot.child("logoUrl").getValue(String.class);
|
||||||
|
if (photoUrl != null && !photoUrl.isEmpty()) {
|
||||||
|
Glide.with(DefinicoesAdminActivity.this).load(photoUrl).circleCrop().into(imgLogo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(DefinicoesAdminActivity.this, "Erro ao carregar definições.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveSettings() {
|
||||||
|
if (documentId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String radiusStr = inputRadius.getText().toString().trim();
|
||||||
|
String addressStr = inputAddress.getText().toString().trim();
|
||||||
|
String selectedCategory = spinnerCategory.getSelectedItem().toString();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(radiusStr) || TextUtils.isEmpty(addressStr)) {
|
||||||
|
Toast.makeText(this, "Preencha todos os campos.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int radius = Integer.parseInt(radiusStr);
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("securityDistance", radius);
|
||||||
|
updates.put("address", addressStr);
|
||||||
|
updates.put("category", selectedCategory);
|
||||||
|
if (photoUrl != null) {
|
||||||
|
updates.put("logoUrl", photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseReference.child(documentId).updateChildren(updates)
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
Toast.makeText(this, "Definições guardadas com sucesso!", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.addOnFailureListener(
|
||||||
|
e -> Toast.makeText(this, "Falha ao guardar definições.", Toast.LENGTH_SHORT).show());
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Toast.makeText(this, "Raio inválido.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DetalhesReservasActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private final List<com.example.pap_teste.models.Reserva> reservas = new ArrayList<>();
|
||||||
|
private ArrayAdapter<String> adapter;
|
||||||
|
private ListView listReservas;
|
||||||
|
private TextView txtInfo;
|
||||||
|
private TextView txtNotas;
|
||||||
|
private TextView txtEstado;
|
||||||
|
private TextView txtMensagem;
|
||||||
|
private Button btnConfirmar, btnRecusar, btnApagar;
|
||||||
|
private int selectedIndex = -1;
|
||||||
|
private String restaurantEmail;
|
||||||
|
private com.google.firebase.database.DatabaseReference databaseReference;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_detalhes_reservas);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.detalhesReservasRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
restaurantEmail = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (restaurantEmail == null)
|
||||||
|
restaurantEmail = "sabor_arte@restaurante.com";
|
||||||
|
|
||||||
|
bindViews();
|
||||||
|
setupList();
|
||||||
|
setupActions();
|
||||||
|
loadReservas();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindViews() {
|
||||||
|
listReservas = findViewById(R.id.listReservas);
|
||||||
|
txtInfo = findViewById(R.id.txtReservaInfo);
|
||||||
|
txtNotas = findViewById(R.id.txtReservaNotas);
|
||||||
|
txtEstado = findViewById(R.id.txtReservaEstado);
|
||||||
|
txtMensagem = findViewById(R.id.txtMensagemReserva);
|
||||||
|
|
||||||
|
btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
||||||
|
btnRecusar = findViewById(R.id.btnCancelarReserva);
|
||||||
|
btnApagar = findViewById(R.id.btnApagarReserva);
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadReservas() {
|
||||||
|
databaseReference = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
databaseReference.orderByChild("restauranteEmail").equalTo(restaurantEmail)
|
||||||
|
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(
|
||||||
|
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
reservas.clear();
|
||||||
|
for (com.google.firebase.database.DataSnapshot data : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Reserva r = data
|
||||||
|
.getValue(com.example.pap_teste.models.Reserva.class);
|
||||||
|
if (r != null && r.getEstado() != null && !"Arquivada".equals(r.getEstado())) {
|
||||||
|
reservas.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(
|
||||||
|
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
Toast.makeText(DetalhesReservasActivity.this, "Erro ao carregar reservas.", Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupList() {
|
||||||
|
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
||||||
|
listReservas.setAdapter(adapter);
|
||||||
|
|
||||||
|
listReservas.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
selectedIndex = position;
|
||||||
|
mostrarDetalhe(reservas.get(position));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActions() {
|
||||||
|
if (btnConfirmar != null) {
|
||||||
|
btnConfirmar.setOnClickListener(v -> {
|
||||||
|
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
||||||
|
if ("Pendente".equals(item.getEstado())) {
|
||||||
|
atualizarEstadoSelecionado("Confirmada");
|
||||||
|
} else if ("Confirmada".equals(item.getEstado())) {
|
||||||
|
atualizarEstadoSelecionado("Concluída");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnRecusar != null) {
|
||||||
|
btnRecusar.setOnClickListener(v -> showRecusarDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnApagar != null) {
|
||||||
|
btnApagar.setOnClickListener(v -> apagarReserva());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showRecusarDialog() {
|
||||||
|
if (selectedIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String[] motivos = { "Sem espaço no restaurante", "Fora de horas", "Reserva duplicada", "Outro" };
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Motivo da Recusa")
|
||||||
|
.setItems(motivos, (dialog, which) -> {
|
||||||
|
atualizarEstadoSelecionado("Recusada (" + motivos[which] + ")");
|
||||||
|
})
|
||||||
|
.setNegativeButton("Voltar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apagarReserva() {
|
||||||
|
if (selectedIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Apagar/Arquivar Reserva")
|
||||||
|
.setMessage("Tem a certeza que deseja limpar esta reserva da lista?")
|
||||||
|
.setPositiveButton("Sim", (dialog, which) -> {
|
||||||
|
com.example.pap_teste.models.Reserva r = reservas.get(selectedIndex);
|
||||||
|
String idReserva = r.getId();
|
||||||
|
|
||||||
|
if ("Concluída".equals(r.getEstado()) || r.getEstado().startsWith("Confirmada")) {
|
||||||
|
databaseReference.child(idReserva).child("estado").setValue("Arquivada").addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
clearSelectionAndShowMessage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
databaseReference.child(idReserva).removeValue().addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
clearSelectionAndShowMessage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("Voltar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearSelectionAndShowMessage() {
|
||||||
|
selectedIndex = -1;
|
||||||
|
txtInfo.setText("Selecione uma reserva");
|
||||||
|
txtNotas.setText("");
|
||||||
|
txtEstado.setText("Estado:");
|
||||||
|
txtMensagem.setText("Reserva processada e apagada da vista.");
|
||||||
|
toggleButtons(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void atualizarEstadoSelecionado(String novoEstado) {
|
||||||
|
if (selectedIndex < 0 || selectedIndex >= reservas.size()) {
|
||||||
|
Toast.makeText(this, "Selecione uma reserva para atualizar.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
||||||
|
databaseReference.child(item.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
txtMensagem
|
||||||
|
.setText(String.format("Reserva de %s marcada como %s.", item.getClienteEmail(), novoEstado));
|
||||||
|
mostrarDetalhe(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarDetalhe(com.example.pap_teste.models.Reserva item) {
|
||||||
|
txtInfo.setText(String.format("%s • %s • %s • %dp", item.getClienteEmail(), item.getRestauranteName(),
|
||||||
|
item.getHora(), item.getPessoas()));
|
||||||
|
txtNotas.setText("Data: " + item.getData());
|
||||||
|
txtEstado.setText(String.format("Estado: %s", item.getEstado()));
|
||||||
|
toggleButtons(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleButtons(com.example.pap_teste.models.Reserva item) {
|
||||||
|
if (item == null) {
|
||||||
|
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.GONE);
|
||||||
|
btnApagar.setVisibility(android.view.View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.getEstado()) {
|
||||||
|
case "Pendente":
|
||||||
|
btnConfirmar.setText("Confirmar");
|
||||||
|
btnConfirmar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
btnApagar.setVisibility(android.view.View.GONE);
|
||||||
|
break;
|
||||||
|
case "Confirmada":
|
||||||
|
btnConfirmar.setText("Concluir");
|
||||||
|
btnConfirmar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.VISIBLE); // Still allow refusal
|
||||||
|
btnApagar.setVisibility(android.view.View.GONE);
|
||||||
|
break;
|
||||||
|
case "Concluída":
|
||||||
|
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.GONE);
|
||||||
|
btnApagar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
break;
|
||||||
|
default: // Recusada or Cancelada
|
||||||
|
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.GONE);
|
||||||
|
btnApagar.setVisibility(android.view.View.VISIBLE); // Allow deleting refused ones as well
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshList() {
|
||||||
|
adapter.clear();
|
||||||
|
for (com.example.pap_teste.models.Reserva item : reservas) {
|
||||||
|
String resumo = String.format("%s - %s (%s) • %s", item.getHora(), item.getData(), item.getEstado(),
|
||||||
|
item.getClienteEmail());
|
||||||
|
adapter.add(resumo);
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
public class EstablishmentDashboardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_establishment_dashboard);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.establishmentRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
TextView txtTitle = findViewById(R.id.txtEstabTitle);
|
||||||
|
TextView txtSubtitle = findViewById(R.id.txtEstabSubtitle);
|
||||||
|
TextView txtRole = findViewById(R.id.txtEstabRole);
|
||||||
|
|
||||||
|
String actionMode = getIntent().getStringExtra(MainActivity.EXTRA_ACTION_MODE);
|
||||||
|
String displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
||||||
|
String role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE);
|
||||||
|
|
||||||
|
boolean isNewAccount = "CRIAR".equalsIgnoreCase(actionMode);
|
||||||
|
txtTitle.setText(displayName != null ? displayName : "Estabelecimento");
|
||||||
|
txtRole.setText(String.format("Função: %s", role != null ? role : "ADMIN"));
|
||||||
|
txtSubtitle.setText(isNewAccount
|
||||||
|
? "Perfil criado. Configure horários, mesas e abra reservas."
|
||||||
|
: "Dashboard operacional. Acompanhe as reservas em tempo real.");
|
||||||
|
|
||||||
|
Button btnOpenWalkIns = findViewById(R.id.btnAbrirEspera);
|
||||||
|
Button btnStaff = findViewById(R.id.btnGestaoStaff);
|
||||||
|
Button btnGerirMesas = findViewById(R.id.btnGerirMesas);
|
||||||
|
Button btnDetails = findViewById(R.id.btnDetalhesReservas);
|
||||||
|
Button btnSettings = findViewById(R.id.btnDefinicoes);
|
||||||
|
Button btnBack = findViewById(R.id.btnVoltar);
|
||||||
|
|
||||||
|
btnOpenWalkIns.setOnClickListener(v -> startActivity(new Intent(this, ListaEsperaActivity.class)));
|
||||||
|
|
||||||
|
btnStaff.setOnClickListener(v -> startActivity(new Intent(this, GestaoStaffActivity.class)));
|
||||||
|
|
||||||
|
btnGerirMesas.setOnClickListener(v -> startActivity(new Intent(this, GerirMesasActivity.class)));
|
||||||
|
|
||||||
|
btnDetails.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(this, DetalhesReservasActivity.class);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL));
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
btnSettings.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(this, DefinicoesAdminActivity.class);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL));
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.cardReservasHoje).setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(this, DetalhesReservasActivity.class);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL));
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (btnBack != null) {
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProximasReservas();
|
||||||
|
loadEstatisticas();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadProximasReservas() {
|
||||||
|
android.widget.LinearLayout llProximasReservas = findViewById(R.id.llProximasReservas);
|
||||||
|
String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (email == null) {
|
||||||
|
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
||||||
|
email = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
String todayDate = cal.get(java.util.Calendar.DAY_OF_MONTH) + "/" +
|
||||||
|
(cal.get(java.util.Calendar.MONTH) + 1) + "/" +
|
||||||
|
cal.get(java.util.Calendar.YEAR);
|
||||||
|
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas")
|
||||||
|
.orderByChild("restauranteEmail").equalTo(email)
|
||||||
|
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
llProximasReservas.removeAllViews();
|
||||||
|
boolean found = false;
|
||||||
|
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Reserva r = ds.getValue(com.example.pap_teste.models.Reserva.class);
|
||||||
|
if (r != null && r.getEstado() != null && (r.getEstado().startsWith("Confirmada") || r.getEstado().equals("Concluída")) && todayDate.equals(r.getData())) {
|
||||||
|
found = true;
|
||||||
|
addReservaView(llProximasReservas, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
TextView empty = new TextView(EstablishmentDashboardActivity.this);
|
||||||
|
empty.setText("Não há reservas confirmadas para hoje.");
|
||||||
|
empty.setTextColor(android.graphics.Color.parseColor("#5F5F5F"));
|
||||||
|
empty.setTextSize(14f);
|
||||||
|
llProximasReservas.addView(empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addReservaView(android.widget.LinearLayout container, com.example.pap_teste.models.Reserva r) {
|
||||||
|
TextView title = new TextView(this);
|
||||||
|
title.setText(String.format("%s - %s", r.getHora(), r.getData()));
|
||||||
|
title.setTextColor(android.graphics.Color.BLACK);
|
||||||
|
title.setTextSize(16f);
|
||||||
|
title.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
|
||||||
|
TextView subtitle = new TextView(this);
|
||||||
|
subtitle.setText(String.format("Cliente: %s • %d pessoas", r.getClienteEmail(), r.getPessoas()));
|
||||||
|
subtitle.setTextColor(android.graphics.Color.parseColor("#5F5F5F"));
|
||||||
|
subtitle.setTextSize(14f);
|
||||||
|
|
||||||
|
android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams(
|
||||||
|
android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
subtitle.setLayoutParams(params);
|
||||||
|
|
||||||
|
android.view.View divider = new android.view.View(this);
|
||||||
|
android.widget.LinearLayout.LayoutParams divParams = new android.widget.LinearLayout.LayoutParams(
|
||||||
|
android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 2);
|
||||||
|
divParams.setMargins(0, 24, 0, 24);
|
||||||
|
divider.setLayoutParams(divParams);
|
||||||
|
divider.setBackgroundColor(android.graphics.Color.parseColor("#EEEEEE"));
|
||||||
|
|
||||||
|
container.addView(title);
|
||||||
|
container.addView(subtitle);
|
||||||
|
container.addView(divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadEstatisticas() {
|
||||||
|
TextView txtReservasHoje = findViewById(R.id.txtReservasHojeDash);
|
||||||
|
TextView txtMesasLivres = findViewById(R.id.txtMesasLivresDash);
|
||||||
|
TextView txtListaEspera = findViewById(R.id.txtListaEsperaDash);
|
||||||
|
|
||||||
|
String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (email == null) {
|
||||||
|
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
||||||
|
email = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String finalEmail = email;
|
||||||
|
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
String todayDate = cal.get(java.util.Calendar.DAY_OF_MONTH) + "/" +
|
||||||
|
(cal.get(java.util.Calendar.MONTH) + 1) + "/" +
|
||||||
|
cal.get(java.util.Calendar.YEAR);
|
||||||
|
|
||||||
|
com.google.firebase.database.DatabaseReference refReservas = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
refReservas.orderByChild("restauranteEmail").equalTo(finalEmail)
|
||||||
|
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
int reservasHoje = 0;
|
||||||
|
int listaEspera = 0;
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Reserva r = ds.getValue(com.example.pap_teste.models.Reserva.class);
|
||||||
|
if (r != null && r.getEstado() != null) {
|
||||||
|
if ((r.getEstado().startsWith("Confirmada") || r.getEstado().equals("Concluída") || r.getEstado().equals("Arquivada")) && todayDate.equals(r.getData())) {
|
||||||
|
reservasHoje++;
|
||||||
|
} else if ("Pendente".equals(r.getEstado())) {
|
||||||
|
listaEspera++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (txtReservasHoje != null) txtReservasHoje.setText(String.format(java.util.Locale.US, "%02d", reservasHoje));
|
||||||
|
if (txtListaEspera != null) txtListaEspera.setText(String.format(java.util.Locale.US, "%02d", listaEspera));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas")
|
||||||
|
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
int mesasLivres = 0;
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Mesa mesa = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
||||||
|
if (mesa != null && finalEmail.equals(mesa.getRestauranteEmail())) {
|
||||||
|
if ("Livre".equalsIgnoreCase(mesa.getEstado())) {
|
||||||
|
mesasLivres++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (txtMesasLivres != null) txtMesasLivres.setText(String.format(java.util.Locale.US, "%02d", mesasLivres));
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
LIST, DETAILS
|
||||||
|
}
|
||||||
|
|
||||||
|
private State currentState = State.LIST;
|
||||||
|
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
||||||
|
|
||||||
|
private android.view.View scrollReservaDetails;
|
||||||
|
private androidx.recyclerview.widget.RecyclerView rvRestaurants;
|
||||||
|
private android.widget.TextView txtTitle;
|
||||||
|
|
||||||
|
private String selectedDate = null;
|
||||||
|
private String selectedTime = null;
|
||||||
|
|
||||||
|
private final androidx.activity.result.ActivityResultLauncher<android.content.Intent> photoPickerLauncher = registerForActivityResult(
|
||||||
|
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
||||||
|
android.net.Uri imageUri = result.getData().getData();
|
||||||
|
if (imageUri != null) {
|
||||||
|
uploadRestaurantPhoto(imageUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_explorar_restaurantes);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.explorarRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
rvRestaurants = findViewById(R.id.rvRestaurants);
|
||||||
|
scrollReservaDetails = findViewById(R.id.scrollReservaDetails);
|
||||||
|
txtTitle = findViewById(R.id.txtTituloExplorar);
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> handleBackNavigation());
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRestaurantList();
|
||||||
|
updateViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBackNavigation() {
|
||||||
|
if (currentState == State.DETAILS) {
|
||||||
|
currentState = State.LIST;
|
||||||
|
updateViewState();
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateViewState() {
|
||||||
|
rvRestaurants.setVisibility(currentState == State.LIST ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||||
|
scrollReservaDetails
|
||||||
|
.setVisibility(currentState == State.DETAILS ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||||
|
|
||||||
|
if (currentState == State.LIST) {
|
||||||
|
String filter = getIntent().getStringExtra("category_filter");
|
||||||
|
txtTitle.setText(filter != null ? "Explorar: " + filter : "Explorar restaurantes");
|
||||||
|
} else {
|
||||||
|
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
||||||
|
setupReservationOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRestaurantList() {
|
||||||
|
String filter = getIntent().getStringExtra("category_filter");
|
||||||
|
java.util.List<com.example.pap_teste.models.Restaurant> restaurantsList = new java.util.ArrayList<>();
|
||||||
|
com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users");
|
||||||
|
|
||||||
|
com.google.firebase.database.Query query = usersRef;
|
||||||
|
if (filter != null) {
|
||||||
|
query = usersRef.orderByChild("category").equalTo(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
query.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
restaurantsList.clear();
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
String role = ds.child("role").getValue(String.class);
|
||||||
|
String accountType = ds.child("accountType").getValue(String.class);
|
||||||
|
|
||||||
|
if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) {
|
||||||
|
String name = ds.child("establishmentName").getValue(String.class);
|
||||||
|
if (name == null) name = ds.child("displayName").getValue(String.class);
|
||||||
|
String email = ds.child("email").getValue(String.class);
|
||||||
|
String cat = ds.child("category").getValue(String.class);
|
||||||
|
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
||||||
|
|
||||||
|
if (name != null && email != null) {
|
||||||
|
restaurantsList.add(new com.example.pap_teste.models.Restaurant(name, cat, email, false, logoUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RestaurantAdapter adapter = new RestaurantAdapter(restaurantsList, restaurant -> {
|
||||||
|
selectedRestaurant = restaurant;
|
||||||
|
currentState = State.DETAILS;
|
||||||
|
updateViewState();
|
||||||
|
});
|
||||||
|
rvRestaurants.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
android.widget.Toast.makeText(ExplorarRestaurantesActivity.this, "Erro ao carregar restaurantes.", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupReservationOptions() {
|
||||||
|
android.widget.Button btnDate = findViewById(R.id.btnSelectDate);
|
||||||
|
android.widget.Button btnTime = findViewById(R.id.btnSelectTime);
|
||||||
|
android.widget.Button btnVerAvaliacoes = findViewById(R.id.btnVerAvaliacoes);
|
||||||
|
android.widget.Button btnUploadFoto = findViewById(R.id.btnUploadFoto);
|
||||||
|
|
||||||
|
btnDate.setOnClickListener(v -> {
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||||
|
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||||
|
btnDate.setText(selectedDate);
|
||||||
|
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH), cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
btnTime.setOnClickListener(v -> {
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||||
|
selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||||
|
btnTime.setText(selectedTime);
|
||||||
|
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation());
|
||||||
|
|
||||||
|
btnVerAvaliacoes.setOnClickListener(v -> showReviewsDialog());
|
||||||
|
|
||||||
|
btnUploadFoto.setOnClickListener(v -> {
|
||||||
|
android.content.Intent intent = new android.content.Intent(android.content.Intent.ACTION_PICK);
|
||||||
|
intent.setType("image/*");
|
||||||
|
photoPickerLauncher.launch(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadRestaurantPhoto(android.net.Uri imageUri) {
|
||||||
|
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||||
|
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
com.google.firebase.storage.StorageReference storageRef = com.google.firebase.storage.FirebaseStorage.getInstance().getReference()
|
||||||
|
.child("restaurant_photos/" + encodedEmail + "/" + java.util.UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
||||||
|
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
||||||
|
String photoUrl = uri.toString();
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("photos").child(encodedEmail)
|
||||||
|
.push().child("url").setValue(photoUrl).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
android.widget.Toast.makeText(this, "Foto adicionada com sucesso!", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).addOnFailureListener(e -> {
|
||||||
|
android.widget.Toast.makeText(this, "Falha no upload: " + e.getMessage(), android.widget.Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showReviewsDialog() {
|
||||||
|
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||||
|
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
|
||||||
|
com.google.firebase.database.DatabaseReference reviewsRef = com.google.firebase.database.FirebaseDatabase.getInstance()
|
||||||
|
.getReference("reviews").child(encodedEmail);
|
||||||
|
|
||||||
|
reviewsRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
java.util.List<String> reviewsList = new java.util.ArrayList<>();
|
||||||
|
for (com.google.firebase.database.DataSnapshot dst : snapshot.getChildren()) {
|
||||||
|
String author = dst.child("author").getValue(String.class);
|
||||||
|
String text = dst.child("text").getValue(String.class);
|
||||||
|
if (author != null && text != null) {
|
||||||
|
reviewsList.add(author + "\n" + text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] reviewsArray = reviewsList.toArray(new String[0]);
|
||||||
|
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(ExplorarRestaurantesActivity.this)
|
||||||
|
.setTitle("Avaliações")
|
||||||
|
.setItems(reviewsArray, null)
|
||||||
|
.setPositiveButton("Fechar", null)
|
||||||
|
.setNeutralButton("Adicionar", (dialog, which) -> addReviewDialog());
|
||||||
|
|
||||||
|
if (reviewsList.isEmpty()) {
|
||||||
|
builder.setMessage("Ainda sem avaliações.");
|
||||||
|
}
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
android.widget.Toast.makeText(ExplorarRestaurantesActivity.this, "Erro ao carregar avaliações.", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addReviewDialog() {
|
||||||
|
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||||
|
|
||||||
|
android.widget.EditText input = new android.widget.EditText(this);
|
||||||
|
input.setHint("Escreva a sua avaliação aqui...");
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Adicionar Avaliação")
|
||||||
|
.setView(input)
|
||||||
|
.setPositiveButton("Enviar", (dialog, which) -> {
|
||||||
|
String revText = input.getText().toString().trim();
|
||||||
|
if (!revText.isEmpty()) {
|
||||||
|
com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
if (currentUser != null && currentUser.getEmail() != null) {
|
||||||
|
String userDoc = currentUser.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users").child(userDoc).get().addOnSuccessListener(snapshot -> {
|
||||||
|
String authorName = snapshot.exists() && snapshot.child("displayName").getValue(String.class) != null
|
||||||
|
? snapshot.child("displayName").getValue(String.class)
|
||||||
|
: "Visitante";
|
||||||
|
submitReviewToFirebase(authorName, revText);
|
||||||
|
}).addOnFailureListener(e -> submitReviewToFirebase("Visitante", revText));
|
||||||
|
} else {
|
||||||
|
submitReviewToFirebase("Visitante", revText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void submitReviewToFirebase(String authorName, String revText) {
|
||||||
|
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
java.util.Map<String, Object> review = new java.util.HashMap<>();
|
||||||
|
review.put("author", authorName);
|
||||||
|
review.put("text", revText);
|
||||||
|
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews")
|
||||||
|
.child(encodedEmail).push().setValue(review).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
android.widget.Toast.makeText(this, "Avaliação enviada!", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveReservation() {
|
||||||
|
android.widget.EditText etPartySize = findViewById(R.id.etPartySize);
|
||||||
|
int partySize = 0;
|
||||||
|
try {
|
||||||
|
partySize = Integer.parseInt(etPartySize.getText().toString());
|
||||||
|
} catch (Exception e) {}
|
||||||
|
|
||||||
|
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
||||||
|
android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.",
|
||||||
|
android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
|
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||||
|
: "cliente@teste.com";
|
||||||
|
|
||||||
|
com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance()
|
||||||
|
.getReference("reservas");
|
||||||
|
String id = ref.push().getKey();
|
||||||
|
|
||||||
|
com.example.pap_teste.models.Reserva reserva = new com.example.pap_teste.models.Reserva(
|
||||||
|
id,
|
||||||
|
userEmail,
|
||||||
|
selectedRestaurant.getName(),
|
||||||
|
selectedRestaurant.getEmail(),
|
||||||
|
selectedDate,
|
||||||
|
selectedTime,
|
||||||
|
partySize,
|
||||||
|
"Pendente");
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
ref.child(id).setValue(reserva).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
android.widget.Toast
|
||||||
|
.makeText(this, "Reserva solicitada com sucesso!", android.widget.Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
android.widget.Toast.makeText(this, "Erro ao salvar reserva.", android.widget.Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FavoritosActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private androidx.recyclerview.widget.RecyclerView rv;
|
||||||
|
private RestaurantAdapter adapter;
|
||||||
|
private List<Restaurant> list;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_favoritos);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.favoritosRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = findViewById(R.id.rvFavoritos);
|
||||||
|
list = new ArrayList<>();
|
||||||
|
adapter = new RestaurantAdapter(list, null);
|
||||||
|
rv.setAdapter(adapter);
|
||||||
|
|
||||||
|
setupFavoritesList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupFavoritesList() {
|
||||||
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
if (user != null && user.getEmail() != null) {
|
||||||
|
String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("users")
|
||||||
|
.child(encodedUserEmail).child("favorites");
|
||||||
|
|
||||||
|
favRef.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
list.clear();
|
||||||
|
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
Restaurant restaurant = ds.getValue(Restaurant.class);
|
||||||
|
if (restaurant != null) {
|
||||||
|
list.add(restaurant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(FavoritosActivity.this, "Erro ao carregar favoritos.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.FoodCategory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FoodCategoryAdapter extends RecyclerView.Adapter<FoodCategoryAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
public interface OnCategoryClickListener {
|
||||||
|
void onCategoryClick(FoodCategory category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<FoodCategory> categories;
|
||||||
|
private final OnCategoryClickListener listener;
|
||||||
|
|
||||||
|
public FoodCategoryAdapter(List<FoodCategory> categories, OnCategoryClickListener listener) {
|
||||||
|
this.categories = categories;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_food_category, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
FoodCategory category = categories.get(position);
|
||||||
|
holder.txtName.setText(category.getName());
|
||||||
|
if (category.getImageResId() != 0) {
|
||||||
|
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||||
|
.load(category.getImageResId())
|
||||||
|
.centerCrop()
|
||||||
|
.into(holder.imgCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onCategoryClick(category);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return categories.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
ImageView imgCategory;
|
||||||
|
TextView txtName;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
imgCategory = itemView.findViewById(R.id.imgCategory);
|
||||||
|
txtName = itemView.findViewById(R.id.txtCategoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
227
app/src/main/java/com/example/pap_teste/GerirMesasActivity.java
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Mesa;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class GerirMesasActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private final List<Mesa> mesas = new ArrayList<>();
|
||||||
|
private ArrayAdapter<String> adapter;
|
||||||
|
private ListView listMesas;
|
||||||
|
private EditText inputNumero;
|
||||||
|
private EditText inputCapacidade;
|
||||||
|
private Spinner spinnerEstado;
|
||||||
|
private TextView txtMensagem;
|
||||||
|
private DatabaseReference mDatabase;
|
||||||
|
private Mesa selectedMesa = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_gerir_mesas);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.gerirMesasRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
bindViews();
|
||||||
|
|
||||||
|
mDatabase = FirebaseDatabase.getInstance().getReference("Mesas");
|
||||||
|
|
||||||
|
setupList();
|
||||||
|
setupFormActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindViews() {
|
||||||
|
listMesas = findViewById(R.id.listMesas);
|
||||||
|
inputNumero = findViewById(R.id.inputMesaNumero);
|
||||||
|
inputCapacidade = findViewById(R.id.inputMesaCapacidade);
|
||||||
|
spinnerEstado = findViewById(R.id.spinnerEstadoMesa);
|
||||||
|
txtMensagem = findViewById(R.id.txtMensagemMesa);
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayAdapter<String> estadoAdapter = new ArrayAdapter<>(
|
||||||
|
this,
|
||||||
|
android.R.layout.simple_spinner_dropdown_item,
|
||||||
|
new String[] { "Livre", "Ocupada", "Reservada" });
|
||||||
|
spinnerEstado.setAdapter(estadoAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupList() {
|
||||||
|
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
||||||
|
listMesas.setAdapter(adapter);
|
||||||
|
|
||||||
|
mDatabase.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
mesas.clear();
|
||||||
|
adapter.clear();
|
||||||
|
String currentUserEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
|
? FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||||
|
: "";
|
||||||
|
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
|
||||||
|
Mesa mesa = postSnapshot.getValue(Mesa.class);
|
||||||
|
if (mesa != null && (mesa.getRestauranteEmail() == null || mesa.getRestauranteEmail().equals(currentUserEmail))) {
|
||||||
|
// Ensure the ID is set from the snapshot key if it's missing in the value
|
||||||
|
mesa.setId(postSnapshot.getKey());
|
||||||
|
mesas.add(mesa);
|
||||||
|
String resumo = String.format("Mesa %02d • %d lugares • %s", mesa.getNumero(),
|
||||||
|
mesa.getCapacidade(), mesa.getEstado());
|
||||||
|
adapter.add(resumo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(GerirMesasActivity.this, "Erro ao carregar mesas: " + error.getMessage(),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
listMesas.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
Mesa item = mesas.get(position);
|
||||||
|
selectedMesa = item;
|
||||||
|
inputNumero.setText(String.valueOf(item.getNumero()));
|
||||||
|
inputCapacidade.setText(String.valueOf(item.getCapacidade()));
|
||||||
|
spinnerEstado.setSelection(getEstadoIndex(item.getEstado()));
|
||||||
|
txtMensagem.setText(String.format("Editar mesa %d", item.getNumero()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getEstadoIndex(String estado) {
|
||||||
|
if ("Ocupada".equalsIgnoreCase(estado)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ("Reservada".equalsIgnoreCase(estado)) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupFormActions() {
|
||||||
|
Button btnGuardar = findViewById(R.id.btnGuardarMesa);
|
||||||
|
if (btnGuardar != null) {
|
||||||
|
btnGuardar.setOnClickListener(v -> guardarMesa());
|
||||||
|
}
|
||||||
|
|
||||||
|
Button btnRemover = findViewById(R.id.btnRemoverMesa);
|
||||||
|
if (btnRemover != null) {
|
||||||
|
btnRemover.setOnClickListener(v -> removerMesa());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removerMesa() {
|
||||||
|
if (selectedMesa == null || selectedMesa.getId() == null) {
|
||||||
|
Toast.makeText(this, "Selecione uma mesa válida para remover.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDatabase.child(selectedMesa.getId()).removeValue()
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
Toast.makeText(this, "Mesa removida com sucesso.", Toast.LENGTH_SHORT).show();
|
||||||
|
limparCampos();
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
Toast.makeText(this, "Erro ao remover mesa: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void limparCampos() {
|
||||||
|
inputNumero.setText("");
|
||||||
|
inputCapacidade.setText("");
|
||||||
|
selectedMesa = null;
|
||||||
|
txtMensagem.setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void guardarMesa() {
|
||||||
|
String numeroStr = inputNumero.getText().toString().trim();
|
||||||
|
String capacidadeStr = inputCapacidade.getText().toString().trim();
|
||||||
|
String estado = (String) spinnerEstado.getSelectedItem();
|
||||||
|
|
||||||
|
if (numeroStr.isEmpty() || capacidadeStr.isEmpty() || estado == null) {
|
||||||
|
Toast.makeText(this, "Preencha número, capacidade e estado.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numero;
|
||||||
|
int capacidade;
|
||||||
|
try {
|
||||||
|
numero = Integer.parseInt(numeroStr);
|
||||||
|
capacidade = Integer.parseInt(capacidadeStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Toast.makeText(this, "Use apenas números válidos.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesa existente = findMesa(numero);
|
||||||
|
String mesaId;
|
||||||
|
String currentUserEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
|
? FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
if (existente == null) {
|
||||||
|
mesaId = mDatabase.push().getKey();
|
||||||
|
Mesa novaMesa = new Mesa(mesaId, numero, capacidade, estado, currentUserEmail);
|
||||||
|
if (mesaId != null) {
|
||||||
|
mDatabase.child(mesaId).setValue(novaMesa);
|
||||||
|
}
|
||||||
|
txtMensagem.setText(String.format("Mesa %d adicionada.", numero));
|
||||||
|
} else {
|
||||||
|
mesaId = existente.getId();
|
||||||
|
if (mesaId == null) {
|
||||||
|
Toast.makeText(this, "Erro ao atualizar: ID não encontrado.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
existente.setCapacidade(capacidade);
|
||||||
|
existente.setEstado(estado);
|
||||||
|
mDatabase.child(mesaId).setValue(existente);
|
||||||
|
txtMensagem.setText(String.format("Mesa %d atualizada.", numero));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clearing inputs
|
||||||
|
limparCampos();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mesa findMesa(int numero) {
|
||||||
|
for (Mesa item : mesas) {
|
||||||
|
if (item.getNumero() == numero) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
295
app/src/main/java/com/example/pap_teste/GestaoStaffActivity.java
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Mesa;
|
||||||
|
import com.example.pap_teste.models.Staff;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GestaoStaffActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private final List<Staff> staffList = new ArrayList<>();
|
||||||
|
private ArrayAdapter<String> staffAdapter;
|
||||||
|
private ListView listStaffMesas;
|
||||||
|
|
||||||
|
private Spinner spinnerNomeStaff;
|
||||||
|
private Spinner spinnerMesaStaff;
|
||||||
|
private TextView txtMensagemStaff;
|
||||||
|
|
||||||
|
private DatabaseReference staffRef;
|
||||||
|
private DatabaseReference mesasRef;
|
||||||
|
|
||||||
|
private List<String> staffNames = new ArrayList<>();
|
||||||
|
private List<Mesa> mesasDisponiveis = new ArrayList<>();
|
||||||
|
private ArrayAdapter<String> staffNameAdapter;
|
||||||
|
private ArrayAdapter<String> mesaSpinnerAdapter;
|
||||||
|
|
||||||
|
private FloatingActionButton floatingActionButton;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_gestao_staff);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.gestaoStaffRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
staffRef = FirebaseDatabase.getInstance().getReference("Staff");
|
||||||
|
mesasRef = FirebaseDatabase.getInstance().getReference("Mesas");
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
bindViews();
|
||||||
|
setupMesaSpinner();
|
||||||
|
setupList();
|
||||||
|
setupFormActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindViews() {
|
||||||
|
listStaffMesas = findViewById(R.id.listStaffMesas);
|
||||||
|
spinnerNomeStaff = findViewById(R.id.spinnerNomeStaff);
|
||||||
|
spinnerMesaStaff = findViewById(R.id.spinnerMesaStaff);
|
||||||
|
txtMensagemStaff = findViewById(R.id.txtMensagemStaff);
|
||||||
|
floatingActionButton = findViewById(R.id.floatingActionButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preenche o spinner com uma lista simples de mesas (1–20).
|
||||||
|
* Mais tarde isto pode ser ligado às mesas reais configuradas em "Gerir Mesas".
|
||||||
|
*/
|
||||||
|
private void setupMesaSpinner() {
|
||||||
|
mesaSpinnerAdapter = new ArrayAdapter<>(
|
||||||
|
this,
|
||||||
|
android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
|
||||||
|
staffNameAdapter = new ArrayAdapter<>(
|
||||||
|
this,
|
||||||
|
android.R.layout.simple_spinner_dropdown_item,
|
||||||
|
staffNames);
|
||||||
|
spinnerNomeStaff.setAdapter(staffNameAdapter);
|
||||||
|
|
||||||
|
loadStaffMembers();
|
||||||
|
loadMesas();
|
||||||
|
|
||||||
|
spinnerMesaStaff.setAdapter(mesaSpinnerAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadMesas() {
|
||||||
|
mesasRef.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
mesasDisponiveis.clear();
|
||||||
|
mesaSpinnerAdapter.clear();
|
||||||
|
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
|
||||||
|
Mesa mesa = postSnapshot.getValue(Mesa.class);
|
||||||
|
if (mesa != null) {
|
||||||
|
mesasDisponiveis.add(mesa);
|
||||||
|
mesaSpinnerAdapter.add("Mesa " + mesa.getNumero());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mesaSpinnerAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(GestaoStaffActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupList() {
|
||||||
|
staffAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
||||||
|
listStaffMesas.setAdapter(staffAdapter);
|
||||||
|
|
||||||
|
listStaffMesas.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
Staff item = staffList.get(position);
|
||||||
|
// Select staff in spinner
|
||||||
|
int staffIndex = staffNames.indexOf(item.getName());
|
||||||
|
if (staffIndex >= 0) {
|
||||||
|
spinnerNomeStaff.setSelection(staffIndex);
|
||||||
|
}
|
||||||
|
// Select mesa in spinner
|
||||||
|
// Simple string matching for now since Mesa is stored as String in Staff
|
||||||
|
String assignedMesa = item.getMesa();
|
||||||
|
if (assignedMesa != null) {
|
||||||
|
for (int i = 0; i < mesaSpinnerAdapter.getCount(); i++) {
|
||||||
|
if (mesaSpinnerAdapter.getItem(i).equals(assignedMesa)) {
|
||||||
|
spinnerMesaStaff.setSelection(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txtMensagemStaff.setText(String.format("A editar: %s", item.getName()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupFormActions() {
|
||||||
|
Button btnAtribuir = findViewById(R.id.btnAtribuirStaff);
|
||||||
|
if (btnAtribuir != null) {
|
||||||
|
btnAtribuir.setOnClickListener(v -> guardarAtribuicao());
|
||||||
|
}
|
||||||
|
|
||||||
|
Button btnEliminar = findViewById(R.id.btnEliminarStaff);
|
||||||
|
if (btnEliminar != null) {
|
||||||
|
btnEliminar.setOnClickListener(v -> eliminarStaff());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floatingActionButton != null) {
|
||||||
|
floatingActionButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent(GestaoStaffActivity.this, AddStaffActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Button btnGerirMesas = findViewById(R.id.btnGerirMesasStaff);
|
||||||
|
if (btnGerirMesas != null) {
|
||||||
|
btnGerirMesas.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(GestaoStaffActivity.this, GerirMesasActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void guardarAtribuicao() {
|
||||||
|
String nome = "";
|
||||||
|
if (spinnerNomeStaff.getSelectedItem() != null) {
|
||||||
|
nome = spinnerNomeStaff.getSelectedItem().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nome.isEmpty()) {
|
||||||
|
Toast.makeText(this, "Selecione um funcionário.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spinnerMesaStaff == null || spinnerMesaStaff.getSelectedItem() == null) {
|
||||||
|
Toast.makeText(this, "Selecione uma mesa.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mesaSelecionada = spinnerMesaStaff.getSelectedItem().toString();
|
||||||
|
|
||||||
|
Staff staffToUpdate = findByNome(nome);
|
||||||
|
if (staffToUpdate != null) {
|
||||||
|
staffToUpdate.setMesa(mesaSelecionada);
|
||||||
|
|
||||||
|
final String finalNome = nome;
|
||||||
|
final String finalMesa = mesaSelecionada;
|
||||||
|
|
||||||
|
staffRef.child(staffToUpdate.getId()).setValue(staffToUpdate)
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
txtMensagemStaff.setText(String.format("%s atribuído à %s.", finalNome, finalMesa));
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
Toast.makeText(this, "Erro ao atualizar: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Erro: Staff não encontrado.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eliminarStaff() {
|
||||||
|
String nome = "";
|
||||||
|
if (spinnerNomeStaff.getSelectedItem() != null) {
|
||||||
|
nome = spinnerNomeStaff.getSelectedItem().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nome.isEmpty()) {
|
||||||
|
Toast.makeText(this, "Selecione um funcionário primeiro.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Staff staffToDelete = findByNome(nome);
|
||||||
|
if (staffToDelete != null) {
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Eliminar Staff")
|
||||||
|
.setMessage("Tem a certeza que deseja eliminar o funcionário " + staffToDelete.getName() + "?")
|
||||||
|
.setPositiveButton("Eliminar", (dialog, which) -> {
|
||||||
|
staffRef.child(staffToDelete.getId()).removeValue()
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
txtMensagemStaff.setText(staffToDelete.getName() + " foi eliminado.");
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
Toast.makeText(this, "Erro ao eliminar: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Staff não encontrado.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Staff findByNome(String nome) {
|
||||||
|
for (Staff item : staffList) {
|
||||||
|
if (item.getName().equalsIgnoreCase(nome)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadStaffMembers() {
|
||||||
|
staffRef.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
staffList.clear();
|
||||||
|
staffNames.clear();
|
||||||
|
staffAdapter.clear();
|
||||||
|
|
||||||
|
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
|
||||||
|
Staff staff = postSnapshot.getValue(Staff.class);
|
||||||
|
if (staff != null && staff.getName() != null) {
|
||||||
|
staffList.add(staff);
|
||||||
|
staffNames.add(staff.getName());
|
||||||
|
|
||||||
|
String mesaInfo = staff.getMesa() != null ? staff.getMesa() : "Sem Mesa";
|
||||||
|
String resumo = String.format("%s • %s • %s", staff.getName(), staff.getZona(), mesaInfo);
|
||||||
|
staffAdapter.add(resumo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
staffNameAdapter.notifyDataSetChanged();
|
||||||
|
staffAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(GestaoStaffActivity.this, "Erro ao carregar staff.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
248
app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ListaEsperaActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private final List<Reserva> reservasPendentes = new ArrayList<>();
|
||||||
|
private ArrayAdapter<String> adapter;
|
||||||
|
private ListView listReservas;
|
||||||
|
private TextView txtInfo, txtNotas, txtMensagem;
|
||||||
|
private Button btnConfirmar, btnRecusar;
|
||||||
|
private int selectedIndex = -1;
|
||||||
|
private String restaurantEmail;
|
||||||
|
private DatabaseReference databaseReference;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_lista_espera);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.listaEsperaRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
restaurantEmail = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (restaurantEmail == null) {
|
||||||
|
// Se o extra não chegou, falha segura
|
||||||
|
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
||||||
|
restaurantEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
||||||
|
} else {
|
||||||
|
restaurantEmail = "sabor_arte@restaurante.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindViews();
|
||||||
|
setupList();
|
||||||
|
setupActions();
|
||||||
|
loadReservasPendentes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindViews() {
|
||||||
|
listReservas = findViewById(R.id.listReservasP);
|
||||||
|
txtInfo = findViewById(R.id.txtReservaInfoP);
|
||||||
|
txtNotas = findViewById(R.id.txtReservaNotasP);
|
||||||
|
txtMensagem = findViewById(R.id.txtMensagemReservaP);
|
||||||
|
btnConfirmar = findViewById(R.id.btnConfirmarReservaP);
|
||||||
|
btnRecusar = findViewById(R.id.btnRecusarReservaP);
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupList() {
|
||||||
|
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
||||||
|
listReservas.setAdapter(adapter);
|
||||||
|
|
||||||
|
listReservas.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
selectedIndex = position;
|
||||||
|
mostrarDetalhe(reservasPendentes.get(position));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActions() {
|
||||||
|
btnConfirmar.setOnClickListener(v -> mostrarMesasDisponiveis());
|
||||||
|
btnRecusar.setOnClickListener(v -> showRecusarDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarMesasDisponiveis() {
|
||||||
|
if (selectedIndex < 0 || selectedIndex >= reservasPendentes.size()) {
|
||||||
|
Toast.makeText(this, "Selecione uma reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Reserva item = reservasPendentes.get(selectedIndex);
|
||||||
|
|
||||||
|
DatabaseReference mesasRef = FirebaseDatabase.getInstance().getReference("Mesas");
|
||||||
|
mesasRef.orderByChild("restauranteEmail").equalTo(restaurantEmail).addListenerForSingleValueEvent(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
||||||
|
List<com.example.pap_teste.models.Mesa> mesasLivres = new ArrayList<>();
|
||||||
|
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
||||||
|
if (m != null && m.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre")) {
|
||||||
|
m.setId(ds.getKey());
|
||||||
|
mesasLivres.add(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesasLivres.isEmpty()) {
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(ListaEsperaActivity.this)
|
||||||
|
.setTitle("Sem mesas disponíveis")
|
||||||
|
.setMessage("Não há mesas livres registadas. Deseja confirmar a reserva mesmo assim (sem lugar reservado)?")
|
||||||
|
.setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada"))
|
||||||
|
.setNegativeButton("Não", null)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] mesaOptions = new String[mesasLivres.size()];
|
||||||
|
for (int i = 0; i < mesasLivres.size(); i++) {
|
||||||
|
com.example.pap_teste.models.Mesa m = mesasLivres.get(i);
|
||||||
|
mesaOptions[i] = String.format("Mesa %d (%d lugares)", m.getNumero(), m.getCapacidade());
|
||||||
|
}
|
||||||
|
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(ListaEsperaActivity.this)
|
||||||
|
.setTitle("Atribuir Mesa")
|
||||||
|
.setItems(mesaOptions, (dialog, which) -> {
|
||||||
|
com.example.pap_teste.models.Mesa selecionada = mesasLivres.get(which);
|
||||||
|
confirmarReservaComMesa(item, selecionada);
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(ListaEsperaActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmarReservaComMesa(Reserva reserva, com.example.pap_teste.models.Mesa mesa) {
|
||||||
|
String novoEstado = "Confirmada (Mesa " + mesa.getNumero() + ")";
|
||||||
|
databaseReference.child(reserva.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
FirebaseDatabase.getInstance().getReference("Mesas").child(mesa.getId()).child("estado").setValue("Reservada")
|
||||||
|
.addOnCompleteListener(t2 -> {
|
||||||
|
Toast.makeText(this, "Reserva aceite e mesa atribuída com sucesso.", Toast.LENGTH_SHORT).show();
|
||||||
|
selectedIndex = -1;
|
||||||
|
toggleButtons(null);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showRecusarDialog() {
|
||||||
|
if (selectedIndex < 0) return;
|
||||||
|
|
||||||
|
String[] motivos = { "Sem espaço no restaurante", "Fora de horas", "Reserva duplicada", "Outro" };
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Motivo da Recusa")
|
||||||
|
.setItems(motivos, (dialog, which) -> {
|
||||||
|
atualizarEstadoSelecionado("Recusada (" + motivos[which] + ")");
|
||||||
|
})
|
||||||
|
.setNegativeButton("Voltar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void atualizarEstadoSelecionado(String novoEstado) {
|
||||||
|
if (selectedIndex < 0 || selectedIndex >= reservasPendentes.size()) {
|
||||||
|
Toast.makeText(this, "Selecione uma reserva para avaliar.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reserva item = reservasPendentes.get(selectedIndex);
|
||||||
|
databaseReference.child(item.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
Toast.makeText(this, "Reserva avaliada com sucesso.", Toast.LENGTH_SHORT).show();
|
||||||
|
selectedIndex = -1;
|
||||||
|
toggleButtons(null);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Erro ao alterar estado da reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mostrarDetalhe(Reserva item) {
|
||||||
|
txtInfo.setText(String.format("%s • %s", item.getClienteEmail(), item.getHora()));
|
||||||
|
txtNotas.setText(String.format("Data: %s • Pessoas: %d", item.getData(), item.getPessoas()));
|
||||||
|
toggleButtons(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleButtons(Reserva item) {
|
||||||
|
if (item == null) {
|
||||||
|
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.GONE);
|
||||||
|
txtInfo.setText("Selecione uma reserva");
|
||||||
|
txtNotas.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
btnConfirmar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadReservasPendentes() {
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
databaseReference.orderByChild("restauranteEmail").equalTo(restaurantEmail)
|
||||||
|
.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
||||||
|
reservasPendentes.clear();
|
||||||
|
for (DataSnapshot data : snapshot.getChildren()) {
|
||||||
|
Reserva r = data.getValue(Reserva.class);
|
||||||
|
if (r != null && "Pendente".equals(r.getEstado())) {
|
||||||
|
reservasPendentes.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(ListaEsperaActivity.this, "Erro ao carregar lista de espera.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshList() {
|
||||||
|
adapter.clear();
|
||||||
|
for (Reserva item : reservasPendentes) {
|
||||||
|
adapter.add(String.format("%s - %dp • %s", item.getHora(), item.getPessoas(), item.getClienteEmail()));
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
572
app/src/main/java/com/example/pap_teste/MainActivity.java
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
|
||||||
|
import com.google.firebase.FirebaseApp;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
public static final String EXTRA_ACTION_MODE = "extra_action_mode";
|
||||||
|
public static final String EXTRA_DISPLAY_NAME = "extra_display_name";
|
||||||
|
public static final String EXTRA_EMAIL = "extra_email";
|
||||||
|
public static final String EXTRA_ACCOUNT_TYPE = "extra_account_type";
|
||||||
|
public static final String EXTRA_ROLE = "extra_role";
|
||||||
|
private static final String PREFS_NAME = "pap_prefs";
|
||||||
|
private static final String KEY_HAS_CREATED_ACCOUNT = "has_created_account";
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
CLIENTE, ESTABELECIMENTO
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountAction {
|
||||||
|
ENTRAR, CRIAR
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccountType selectedAccountType = AccountType.CLIENTE;
|
||||||
|
private AccountAction selectedAccountAction = AccountAction.ENTRAR;
|
||||||
|
|
||||||
|
private Button btnCliente;
|
||||||
|
private Button btnEstabelecimento;
|
||||||
|
private Button btnEntrar;
|
||||||
|
private Button btnCriarConta;
|
||||||
|
private Button btnPrimaryAction;
|
||||||
|
private EditText inputName;
|
||||||
|
private EditText inputEmail;
|
||||||
|
private EditText inputPassword;
|
||||||
|
private EditText inputOwnerPhone;
|
||||||
|
private EditText inputEstablishmentName;
|
||||||
|
private EditText inputEstablishmentEmail;
|
||||||
|
private EditText inputEstablishmentPhone;
|
||||||
|
private android.widget.TextView txtForgotPassword;
|
||||||
|
private android.widget.ImageView iconPasswordVisibility;
|
||||||
|
private boolean isPasswordVisible = false;
|
||||||
|
private boolean hasCreatedAccount;
|
||||||
|
private FirebaseAuth firebaseAuth;
|
||||||
|
private DatabaseReference databaseReference;
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String[]> permissionRequest = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.RequestMultiplePermissions(), result -> {
|
||||||
|
Boolean fineLocationGranted = result.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false);
|
||||||
|
Boolean bluetoothScanGranted = true;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
bluetoothScanGranted = result.getOrDefault(Manifest.permission.BLUETOOTH_SCAN, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fineLocationGranted != null && fineLocationGranted) {
|
||||||
|
// Precise location access granted.
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "A permissão de localização é necessária para o check-in.", Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
FirebaseApp.initializeApp(this);
|
||||||
|
firebaseAuth = FirebaseAuth.getInstance();
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference();
|
||||||
|
|
||||||
|
bindViews();
|
||||||
|
setupTypeToggle();
|
||||||
|
setupActionToggle();
|
||||||
|
setupPrimaryAction();
|
||||||
|
checkPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPermissions() {
|
||||||
|
List<String> permissionsNeeded = new ArrayList<>();
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
|
||||||
|
}
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.READ_MEDIA_IMAGES);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!permissionsNeeded.isEmpty()) {
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Permissões Necessárias")
|
||||||
|
.setMessage("Para o correto funcionamento do check-in, serviços de proximidade e fotos da galeria, precisamos de algumas permissões.")
|
||||||
|
.setPositiveButton("Configurar", (dialog, which) -> {
|
||||||
|
permissionRequest.launch(permissionsNeeded.toArray(new String[0]));
|
||||||
|
})
|
||||||
|
.setNegativeButton("Agora Não", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindViews() {
|
||||||
|
btnCliente = findViewById(R.id.btnCliente);
|
||||||
|
btnEstabelecimento = findViewById(R.id.btnEstabelecimento);
|
||||||
|
btnEntrar = findViewById(R.id.btnEntrar);
|
||||||
|
btnCriarConta = findViewById(R.id.btnCriarConta);
|
||||||
|
btnPrimaryAction = findViewById(R.id.btnFinalCriarConta);
|
||||||
|
inputName = findViewById(R.id.inputName);
|
||||||
|
inputEmail = findViewById(R.id.inputEmail);
|
||||||
|
inputPassword = findViewById(R.id.inputPassword);
|
||||||
|
inputOwnerPhone = findViewById(R.id.inputOwnerPhone);
|
||||||
|
inputEstablishmentName = findViewById(R.id.inputEstablishmentName);
|
||||||
|
inputEstablishmentEmail = findViewById(R.id.inputEstablishmentEmail);
|
||||||
|
inputEstablishmentPhone = findViewById(R.id.inputEstablishmentPhone);
|
||||||
|
txtForgotPassword = findViewById(R.id.txtForgotPassword);
|
||||||
|
iconPasswordVisibility = findViewById(R.id.iconPasswordVisibility);
|
||||||
|
|
||||||
|
setupPasswordFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupPasswordFeatures() {
|
||||||
|
iconPasswordVisibility.setOnClickListener(v -> {
|
||||||
|
isPasswordVisible = !isPasswordVisible;
|
||||||
|
if (isPasswordVisible) {
|
||||||
|
inputPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
|
||||||
|
iconPasswordVisibility.setImageResource(R.drawable.ic_visibility);
|
||||||
|
} else {
|
||||||
|
inputPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
|
iconPasswordVisibility.setImageResource(R.drawable.ic_visibility_off);
|
||||||
|
}
|
||||||
|
inputPassword.setSelection(inputPassword.getText().length());
|
||||||
|
});
|
||||||
|
|
||||||
|
txtForgotPassword.setOnClickListener(v -> {
|
||||||
|
String email = inputEmail.getText().toString().trim();
|
||||||
|
if (TextUtils.isEmpty(email)) {
|
||||||
|
Toast.makeText(this, "Por favor, introduza o seu email primeiro.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Recuperar palavra-passe")
|
||||||
|
.setMessage("Deseja enviar um email de recuperação para " + email + "?")
|
||||||
|
.setPositiveButton("Sim", (dialog, which) -> {
|
||||||
|
if (firebaseAuth != null) {
|
||||||
|
firebaseAuth.sendPasswordResetEmail(email)
|
||||||
|
.addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
Toast.makeText(MainActivity.this, "Email de recuperação enviado!", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(MainActivity.this, "Falha ao enviar email. Verifique se o endereço está correto.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("Não", null)
|
||||||
|
.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTypeToggle() {
|
||||||
|
btnCliente.setOnClickListener(v -> {
|
||||||
|
selectedAccountType = AccountType.CLIENTE;
|
||||||
|
updateTypeButtons();
|
||||||
|
});
|
||||||
|
btnEstabelecimento.setOnClickListener(v -> {
|
||||||
|
selectedAccountType = AccountType.ESTABELECIMENTO;
|
||||||
|
updateTypeButtons();
|
||||||
|
});
|
||||||
|
updateTypeButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActionToggle() {
|
||||||
|
btnEntrar.setOnClickListener(v -> {
|
||||||
|
selectedAccountAction = AccountAction.ENTRAR;
|
||||||
|
updateActionButtons();
|
||||||
|
});
|
||||||
|
btnCriarConta.setOnClickListener(v -> {
|
||||||
|
selectedAccountAction = AccountAction.CRIAR;
|
||||||
|
updateActionButtons();
|
||||||
|
});
|
||||||
|
updateActionButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupPrimaryAction() {
|
||||||
|
btnPrimaryAction.setOnClickListener(v -> handlePrimaryAction());
|
||||||
|
updatePrimaryActionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTypeButtons() {
|
||||||
|
setSelectedState(btnCliente, selectedAccountType == AccountType.CLIENTE);
|
||||||
|
setSelectedState(btnEstabelecimento, selectedAccountType == AccountType.ESTABELECIMENTO);
|
||||||
|
updateInputVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateActionButtons() {
|
||||||
|
setSelectedState(btnEntrar, selectedAccountAction == AccountAction.ENTRAR);
|
||||||
|
setSelectedState(btnCriarConta, selectedAccountAction == AccountAction.CRIAR);
|
||||||
|
updatePrimaryActionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSelectedState(Button button, boolean isSelected) {
|
||||||
|
int selectedTextColor = Color.WHITE;
|
||||||
|
int defaultTextColor = Color.parseColor("#231F1F");
|
||||||
|
button.setBackgroundResource(isSelected ? R.drawable.tab_selected : R.drawable.tab_unselected);
|
||||||
|
button.setTextColor(isSelected ? selectedTextColor : defaultTextColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePrimaryActionState() {
|
||||||
|
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
||||||
|
btnPrimaryAction.setText(creatingAccount ? "Criar Conta" : "Entrar");
|
||||||
|
updateInputVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInputVisibility() {
|
||||||
|
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
||||||
|
boolean isEstablishment = selectedAccountType == AccountType.ESTABELECIMENTO;
|
||||||
|
|
||||||
|
inputName.setHint(isEstablishment ? "Nome do proprietário" : "O seu nome");
|
||||||
|
inputEmail.setHint(isEstablishment ? "Email do proprietário" : "Email");
|
||||||
|
|
||||||
|
inputName.setVisibility(creatingAccount ? View.VISIBLE : View.GONE);
|
||||||
|
inputOwnerPhone.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
||||||
|
inputEstablishmentName.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
||||||
|
inputEstablishmentEmail.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
||||||
|
inputEstablishmentPhone.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (txtForgotPassword != null) {
|
||||||
|
txtForgotPassword.setVisibility(creatingAccount ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePrimaryAction() {
|
||||||
|
String email = inputEmail.getText().toString().trim();
|
||||||
|
String password = inputPassword.getText().toString().trim();
|
||||||
|
String providedName = inputName.getText().toString().trim();
|
||||||
|
String ownerPhone = inputOwnerPhone.getText().toString().trim();
|
||||||
|
String establishmentName = inputEstablishmentName.getText().toString().trim();
|
||||||
|
String establishmentEmail = inputEstablishmentEmail.getText().toString().trim();
|
||||||
|
String establishmentPhone = inputEstablishmentPhone.getText().toString().trim();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(password)) {
|
||||||
|
Toast.makeText(this, "Preencha email e palavra-passe.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
||||||
|
|
||||||
|
if (creatingAccount && !isValidPassword(password)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creatingAccount) {
|
||||||
|
if (selectedAccountType == AccountType.CLIENTE && TextUtils.isEmpty(providedName)) {
|
||||||
|
Toast.makeText(this, "Indique o seu nome para criar conta.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedAccountType == AccountType.ESTABELECIMENTO) {
|
||||||
|
boolean missingOwner = TextUtils.isEmpty(providedName) || TextUtils.isEmpty(ownerPhone);
|
||||||
|
boolean missingEstablishment = TextUtils.isEmpty(establishmentName)
|
||||||
|
|| TextUtils.isEmpty(establishmentEmail)
|
||||||
|
|| TextUtils.isEmpty(establishmentPhone);
|
||||||
|
|
||||||
|
if (missingOwner || missingEstablishment) {
|
||||||
|
Toast.makeText(this, "Preencha os dados do proprietário e do estabelecimento.", Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String fallbackName = !TextUtils.isEmpty(providedName) ? providedName : deriveNameFromEmail(email);
|
||||||
|
String resolvedRole = getRoleForSelectedType();
|
||||||
|
|
||||||
|
if (creatingAccount) {
|
||||||
|
createAccountInFirebase(
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
providedName,
|
||||||
|
ownerPhone,
|
||||||
|
establishmentName,
|
||||||
|
establishmentEmail,
|
||||||
|
establishmentPhone,
|
||||||
|
fallbackName,
|
||||||
|
resolvedRole);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
signInExistingAccount(email, password, fallbackName, resolvedRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String deriveNameFromEmail(String email) {
|
||||||
|
if (!email.contains("@")) {
|
||||||
|
return "Utilizador";
|
||||||
|
}
|
||||||
|
String candidate = email.substring(0, email.indexOf("@"));
|
||||||
|
if (candidate.isEmpty()) {
|
||||||
|
return "Utilizador";
|
||||||
|
}
|
||||||
|
String firstLetter = candidate.substring(0, 1).toUpperCase();
|
||||||
|
String rest = candidate.length() > 1 ? candidate.substring(1) : "";
|
||||||
|
return firstLetter + rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markAccountCreated() {
|
||||||
|
hasCreatedAccount = true;
|
||||||
|
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
||||||
|
prefs.edit().putBoolean(KEY_HAS_CREATED_ACCOUNT, true).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRoleForSelectedType() {
|
||||||
|
return selectedAccountType == AccountType.ESTABELECIMENTO ? "ADMIN" : "CLIENTE";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDocumentId(String email) {
|
||||||
|
return email.replace(".", "_").replace("@", "_at_");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ensureFirebaseReady() {
|
||||||
|
boolean ready = firebaseAuth != null && databaseReference != null;
|
||||||
|
if (!ready) {
|
||||||
|
Toast.makeText(this, "Ligue-se ao Firebase para continuar.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void signInExistingAccount(String email, String password, String fallbackName, String resolvedRole) {
|
||||||
|
if (!ensureFirebaseReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
firebaseAuth.signInWithEmailAndPassword(email, password)
|
||||||
|
.addOnSuccessListener(authResult -> fetchAccountAndNavigate(email, fallbackName, resolvedRole))
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
android.util.Log.e("LoginError", "SignIn failed", e);
|
||||||
|
Toast.makeText(this, "Não foi possível iniciar sessão: " + e.getMessage(), Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createAccountInFirebase(
|
||||||
|
String email,
|
||||||
|
String password,
|
||||||
|
String providedName,
|
||||||
|
String ownerPhone,
|
||||||
|
String establishmentName,
|
||||||
|
String establishmentEmail,
|
||||||
|
String establishmentPhone,
|
||||||
|
String fallbackName,
|
||||||
|
String resolvedRole) {
|
||||||
|
if (!ensureFirebaseReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
firebaseAuth.createUserWithEmailAndPassword(email, password)
|
||||||
|
.addOnSuccessListener(result -> {
|
||||||
|
if (!hasCreatedAccount) {
|
||||||
|
markAccountCreated();
|
||||||
|
}
|
||||||
|
String finalDisplayName = selectedAccountType == AccountType.ESTABELECIMENTO
|
||||||
|
&& !TextUtils.isEmpty(establishmentName)
|
||||||
|
? establishmentName
|
||||||
|
: fallbackName;
|
||||||
|
String uid = result.getUser() != null ? result.getUser().getUid() : null;
|
||||||
|
|
||||||
|
persistAccountInFirebase(
|
||||||
|
email,
|
||||||
|
providedName,
|
||||||
|
resolvedRole,
|
||||||
|
ownerPhone,
|
||||||
|
establishmentName,
|
||||||
|
establishmentEmail,
|
||||||
|
establishmentPhone,
|
||||||
|
uid,
|
||||||
|
() -> {
|
||||||
|
Toast.makeText(this, "Conta criada com sucesso! Carregue em Entrar.", Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
selectedAccountAction = AccountAction.ENTRAR;
|
||||||
|
updateActionButtons();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> {
|
||||||
|
android.util.Log.e("LoginError", "CreateUser failed", e);
|
||||||
|
Toast.makeText(this, "Falha ao criar conta: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchAccountAndNavigate(String email, String fallbackName, String resolvedRole) {
|
||||||
|
if (databaseReference == null) {
|
||||||
|
Toast.makeText(this, "Firebase indisponível.", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateToDashboard(email, fallbackName, resolvedRole);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String documentId = buildDocumentId(email);
|
||||||
|
databaseReference.child("users").child(documentId).get().addOnCompleteListener(task -> {
|
||||||
|
if (!task.isSuccessful()) {
|
||||||
|
android.util.Log.e("LoginError", "Database check failed", task.getException());
|
||||||
|
Toast.makeText(this, "Falha ao validar perfil na cloud. A entrar em modo básico.", Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
navigateToDashboard(email, fallbackName, resolvedRole);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataSnapshot snapshot = task.getResult();
|
||||||
|
if (snapshot == null || !snapshot.exists()) {
|
||||||
|
// Toast.makeText(this, "Conta sem perfil na cloud. A entrar em modo básico.",
|
||||||
|
// Toast.LENGTH_SHORT).show();
|
||||||
|
navigateToDashboard(email, fallbackName, resolvedRole);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String accountTypeInFirebase = snapshot.child("accountType").getValue(String.class);
|
||||||
|
if (accountTypeInFirebase != null
|
||||||
|
&& !accountTypeInFirebase.equalsIgnoreCase(selectedAccountType.name())) {
|
||||||
|
Toast.makeText(this, "Tipo de conta não corresponde ao registo no Firebase.", Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String displayNameFromDb = snapshot.child("displayName").getValue(String.class);
|
||||||
|
String establishmentName = snapshot.child("establishmentName").getValue(String.class);
|
||||||
|
String ownerName = snapshot.child("ownerName").getValue(String.class);
|
||||||
|
String roleFromDb = snapshot.child("role").getValue(String.class);
|
||||||
|
|
||||||
|
String finalDisplayName = !TextUtils.isEmpty(establishmentName)
|
||||||
|
? establishmentName
|
||||||
|
: !TextUtils.isEmpty(displayNameFromDb) ? displayNameFromDb
|
||||||
|
: !TextUtils.isEmpty(ownerName) ? ownerName
|
||||||
|
: fallbackName;
|
||||||
|
|
||||||
|
String finalRole = !TextUtils.isEmpty(roleFromDb) ? roleFromDb : resolvedRole;
|
||||||
|
navigateToDashboard(email, finalDisplayName, finalRole);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateToDashboard(String email, String displayName, String role) {
|
||||||
|
Intent nextScreen = selectedAccountType == AccountType.CLIENTE
|
||||||
|
? new Intent(this, ClientDashboardActivity.class)
|
||||||
|
: new Intent(this, EstablishmentDashboardActivity.class);
|
||||||
|
|
||||||
|
nextScreen.putExtra(EXTRA_ACTION_MODE, selectedAccountAction.name());
|
||||||
|
nextScreen.putExtra(EXTRA_DISPLAY_NAME, displayName);
|
||||||
|
nextScreen.putExtra(EXTRA_EMAIL, email);
|
||||||
|
nextScreen.putExtra(EXTRA_ACCOUNT_TYPE, selectedAccountType.name());
|
||||||
|
nextScreen.putExtra(EXTRA_ROLE, role);
|
||||||
|
startActivity(nextScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistAccountInFirebase(
|
||||||
|
String ownerEmail,
|
||||||
|
String ownerName,
|
||||||
|
String role,
|
||||||
|
String ownerPhone,
|
||||||
|
String establishmentName,
|
||||||
|
String establishmentEmail,
|
||||||
|
String establishmentPhone,
|
||||||
|
String uid,
|
||||||
|
Runnable onSuccess) {
|
||||||
|
if (databaseReference == null) {
|
||||||
|
Toast.makeText(this, "Firebase indisponível.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String documentId = buildDocumentId(ownerEmail);
|
||||||
|
Map<String, Object> payload = new HashMap<>();
|
||||||
|
if (!TextUtils.isEmpty(uid)) {
|
||||||
|
payload.put("uid", uid);
|
||||||
|
}
|
||||||
|
payload.put("email", ownerEmail);
|
||||||
|
payload.put("displayName", ownerName);
|
||||||
|
payload.put("role", role);
|
||||||
|
payload.put("accountType", selectedAccountType.name());
|
||||||
|
payload.put("createdAt", System.currentTimeMillis());
|
||||||
|
|
||||||
|
if (selectedAccountType == AccountType.ESTABELECIMENTO) {
|
||||||
|
payload.put("ownerName", ownerName);
|
||||||
|
payload.put("ownerEmail", ownerEmail);
|
||||||
|
payload.put("ownerPhone", ownerPhone);
|
||||||
|
payload.put("establishmentName", establishmentName);
|
||||||
|
payload.put("establishmentEmail", establishmentEmail);
|
||||||
|
payload.put("establishmentPhone", establishmentPhone);
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseReference.child("users").child(documentId).updateChildren(payload)
|
||||||
|
.addOnSuccessListener(unused -> {
|
||||||
|
Toast.makeText(this, "Conta guardada na cloud.", Toast.LENGTH_SHORT).show();
|
||||||
|
if (onSuccess != null) {
|
||||||
|
onSuccess.run();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addOnFailureListener(
|
||||||
|
e -> Toast.makeText(this, "Não foi possível guardar na cloud.", Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidPassword(String password) {
|
||||||
|
if (password.length() < 6) {
|
||||||
|
Toast.makeText(this, "A palavra-passe deve ter pelo menos 6 caracteres.", Toast.LENGTH_SHORT).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasLower = false;
|
||||||
|
boolean hasDigit = false;
|
||||||
|
boolean hasSpecial = false;
|
||||||
|
|
||||||
|
for (char c : password.toCharArray()) {
|
||||||
|
if (Character.isLowerCase(c))
|
||||||
|
hasLower = true;
|
||||||
|
else if (Character.isDigit(c))
|
||||||
|
hasDigit = true;
|
||||||
|
else if (!Character.isLetterOrDigit(c))
|
||||||
|
hasSpecial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLower || !hasDigit || !hasSpecial) {
|
||||||
|
Toast.makeText(this, "A palavra-passe deve conter minúsculas, números e símbolos.", Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MinhasReservasActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private RecyclerView rvMinhasReservas;
|
||||||
|
private ReservaAdapter adapter;
|
||||||
|
private final List<Reserva> reservaList = new ArrayList<>();
|
||||||
|
private DatabaseReference databaseReference;
|
||||||
|
private String clientEmail;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_minhas_reservas);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.minhasReservasRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
clientEmail = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (clientEmail == null) {
|
||||||
|
com.google.firebase.auth.FirebaseUser user = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
if (user != null) {
|
||||||
|
clientEmail = user.getEmail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for testing if still null
|
||||||
|
if (clientEmail == null) {
|
||||||
|
clientEmail = "cliente@teste.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
rvMinhasReservas = findViewById(R.id.rvMinhasReservas);
|
||||||
|
Button btnVoltar = findViewById(R.id.btnVoltar);
|
||||||
|
btnVoltar.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
setupAdapter();
|
||||||
|
loadReservations();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupAdapter() {
|
||||||
|
adapter = new ReservaAdapter(reservaList, new ReservaAdapter.OnReservaActionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckIn(Reserva reserva) {
|
||||||
|
Intent intent = new Intent(MinhasReservasActivity.this, CheckInAntecipadoActivity.class);
|
||||||
|
intent.putExtra("restaurant_email", reserva.getRestauranteEmail());
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel(Reserva reserva) {
|
||||||
|
cancelReservation(reserva);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rvMinhasReservas.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadReservations() {
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
databaseReference.orderByChild("clienteEmail").equalTo(clientEmail)
|
||||||
|
.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
reservaList.clear();
|
||||||
|
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
|
||||||
|
Reserva reserva = dataSnapshot.getValue(Reserva.class);
|
||||||
|
if (reserva != null) {
|
||||||
|
reservaList.add(reserva);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {
|
||||||
|
Toast.makeText(MinhasReservasActivity.this, "Erro ao carregar reservas.", Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelReservation(Reserva reserva) {
|
||||||
|
databaseReference.child(reserva.getId()).child("estado").setValue("Cancelada")
|
||||||
|
.addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Erro ao cancelar reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
291
app/src/main/java/com/example/pap_teste/NovaReservaActivity.java
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
public class NovaReservaActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
CATEGORIES, RESTAURANTS, DETAILS
|
||||||
|
}
|
||||||
|
|
||||||
|
private State currentState = State.CATEGORIES;
|
||||||
|
private String selectedCategory = null;
|
||||||
|
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
||||||
|
|
||||||
|
private androidx.recyclerview.widget.RecyclerView rvCategories, rvRestaurants;
|
||||||
|
private android.view.View scrollNovaReserva;
|
||||||
|
private android.widget.TextView txtTitle;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_nova_reserva);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.novaReservaRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
rvCategories = findViewById(R.id.rvCategories);
|
||||||
|
rvRestaurants = findViewById(R.id.rvRestaurants);
|
||||||
|
scrollNovaReserva = findViewById(R.id.scrollNovaReserva);
|
||||||
|
txtTitle = findViewById(R.id.txtTituloNovaReserva);
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> handleBackNavigation());
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCategories();
|
||||||
|
updateViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBackNavigation() {
|
||||||
|
if (currentState == State.RESTAURANTS) {
|
||||||
|
currentState = State.CATEGORIES;
|
||||||
|
updateViewState();
|
||||||
|
} else if (currentState == State.DETAILS) {
|
||||||
|
currentState = State.RESTAURANTS;
|
||||||
|
updateViewState();
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateViewState() {
|
||||||
|
rvCategories
|
||||||
|
.setVisibility(currentState == State.CATEGORIES ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||||
|
rvRestaurants
|
||||||
|
.setVisibility(currentState == State.RESTAURANTS ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||||
|
scrollNovaReserva
|
||||||
|
.setVisibility(currentState == State.DETAILS ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||||
|
|
||||||
|
if (currentState == State.CATEGORIES) {
|
||||||
|
txtTitle.setText("Escolha o tema");
|
||||||
|
} else if (currentState == State.RESTAURANTS) {
|
||||||
|
txtTitle.setText("Restaurantes: " + selectedCategory);
|
||||||
|
} else {
|
||||||
|
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
||||||
|
setupReservationOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCategories() {
|
||||||
|
java.util.List<com.example.pap_teste.models.FoodCategory> cats = new java.util.ArrayList<>();
|
||||||
|
cats.add(new com.example.pap_teste.models.FoodCategory("Carnes", R.drawable.cat_carnes));
|
||||||
|
cats.add(new com.example.pap_teste.models.FoodCategory("Massas", R.drawable.cat_massas));
|
||||||
|
cats.add(new com.example.pap_teste.models.FoodCategory("Sushi", R.drawable.cat_sushi));
|
||||||
|
cats.add(new com.example.pap_teste.models.FoodCategory("Pizzas", R.drawable.cat_pizzas));
|
||||||
|
cats.add(new com.example.pap_teste.models.FoodCategory("Sobremesas", R.drawable.cat_sobremesas));
|
||||||
|
|
||||||
|
rvCategories.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this));
|
||||||
|
rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> {
|
||||||
|
selectedCategory = category.getName();
|
||||||
|
currentState = State.RESTAURANTS;
|
||||||
|
setupRestaurants();
|
||||||
|
updateViewState();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRestaurants() {
|
||||||
|
java.util.List<com.example.pap_teste.models.Restaurant> filteredList = new java.util.ArrayList<>();
|
||||||
|
com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users");
|
||||||
|
|
||||||
|
usersRef.orderByChild("category").equalTo(selectedCategory).addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
filteredList.clear();
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
String role = ds.child("role").getValue(String.class);
|
||||||
|
String accountType = ds.child("accountType").getValue(String.class);
|
||||||
|
|
||||||
|
if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) {
|
||||||
|
String name = ds.child("establishmentName").getValue(String.class);
|
||||||
|
if (name == null) name = ds.child("displayName").getValue(String.class);
|
||||||
|
String email = ds.child("email").getValue(String.class);
|
||||||
|
String cat = ds.child("category").getValue(String.class);
|
||||||
|
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
||||||
|
|
||||||
|
if (name != null && email != null) {
|
||||||
|
filteredList.add(new com.example.pap_teste.models.Restaurant(name, cat, email, false, logoUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rvRestaurants.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(NovaReservaActivity.this));
|
||||||
|
rvRestaurants.setAdapter(new RestaurantAdapter(filteredList, restaurant -> {
|
||||||
|
selectedRestaurant = restaurant;
|
||||||
|
currentState = State.DETAILS;
|
||||||
|
updateViewState();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
android.widget.Toast.makeText(NovaReservaActivity.this, "Erro ao carregar restaurantes.", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectedDate = null;
|
||||||
|
private String selectedTime = null;
|
||||||
|
|
||||||
|
private void setupReservationOptions() {
|
||||||
|
android.widget.Button btnDate = findViewById(R.id.btnSelectDate);
|
||||||
|
android.widget.Button btnTime = findViewById(R.id.btnSelectTime);
|
||||||
|
|
||||||
|
btnDate.setOnClickListener(v -> {
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||||
|
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||||
|
btnDate.setText(selectedDate);
|
||||||
|
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH), cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
btnTime.setOnClickListener(v -> {
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||||
|
selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||||
|
btnTime.setText(selectedTime);
|
||||||
|
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveReservation() {
|
||||||
|
android.widget.EditText etPartySize = findViewById(R.id.etPartySize);
|
||||||
|
int val = 0;
|
||||||
|
try {
|
||||||
|
val = Integer.parseInt(etPartySize.getText().toString());
|
||||||
|
} catch (Exception e) {}
|
||||||
|
final int partySize = val;
|
||||||
|
|
||||||
|
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
||||||
|
android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.",
|
||||||
|
android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String restEmail = selectedRestaurant.getEmail();
|
||||||
|
|
||||||
|
com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas");
|
||||||
|
|
||||||
|
mesasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
int totalMesas = 0;
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
||||||
|
if (m != null && restEmail.equals(m.getRestauranteEmail())) {
|
||||||
|
totalMesas++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalMesas == 0) {
|
||||||
|
proceedWithReservation(partySize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkReservationsAndSave(totalMesas, partySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
proceedWithReservation(partySize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkReservationsAndSave(int totalMesas, final int partySize) {
|
||||||
|
String restEmail = selectedRestaurant.getEmail();
|
||||||
|
com.google.firebase.database.DatabaseReference reservasRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
|
||||||
|
reservasRef.orderByChild("restauranteEmail").equalTo(restEmail).addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
int ocupadas = 0;
|
||||||
|
java.util.Map<String, Integer> ocupacaoPorHora = new java.util.HashMap<>();
|
||||||
|
|
||||||
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
|
com.example.pap_teste.models.Reserva r = ds.getValue(com.example.pap_teste.models.Reserva.class);
|
||||||
|
if (r != null && selectedDate.equals(r.getData()) && !"Cancelada".equals(r.getEstado()) && !"Recusada".equals(r.getEstado())) {
|
||||||
|
int count = ocupacaoPorHora.getOrDefault(r.getHora(), 0) + 1;
|
||||||
|
ocupacaoPorHora.put(r.getHora(), count);
|
||||||
|
if (selectedTime.equals(r.getHora())) {
|
||||||
|
ocupadas++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ocupadas >= totalMesas) {
|
||||||
|
String sugestao = "";
|
||||||
|
String[] horasComuns = {"12:00", "12:30", "13:00", "13:30", "14:00", "19:00", "19:30", "20:00", "20:30", "21:00", "21:30", "22:00"};
|
||||||
|
for (String h : horasComuns) {
|
||||||
|
if (ocupacaoPorHora.getOrDefault(h, 0) < totalMesas && !h.equals(selectedTime)) {
|
||||||
|
sugestao = h;
|
||||||
|
break; // Encontramos a primeira sugestão livre
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String msg = "Não há mesas disponíveis para as " + selectedTime + ".";
|
||||||
|
if (!sugestao.isEmpty()) {
|
||||||
|
msg += " Sugestão: tente reservar para as " + sugestao + ".";
|
||||||
|
} else {
|
||||||
|
msg += " Tente para outro dia.";
|
||||||
|
}
|
||||||
|
android.widget.Toast.makeText(NovaReservaActivity.this, msg, android.widget.Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
proceedWithReservation(partySize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
proceedWithReservation(partySize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void proceedWithReservation(int partySize) {
|
||||||
|
String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
|
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||||
|
: "cliente@teste.com";
|
||||||
|
com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance()
|
||||||
|
.getReference("reservas");
|
||||||
|
String id = ref.push().getKey();
|
||||||
|
|
||||||
|
com.example.pap_teste.models.Reserva reserva = new com.example.pap_teste.models.Reserva(
|
||||||
|
id,
|
||||||
|
userEmail,
|
||||||
|
selectedRestaurant.getName(),
|
||||||
|
selectedRestaurant.getEmail(),
|
||||||
|
selectedDate,
|
||||||
|
selectedTime,
|
||||||
|
partySize,
|
||||||
|
"Pendente");
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
ref.child(id).setValue(reserva).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
android.widget.Toast
|
||||||
|
.makeText(NovaReservaActivity.this, "Reserva solicitada com sucesso!", android.widget.Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
android.widget.Toast.makeText(NovaReservaActivity.this, "Erro ao salvar reserva.", android.widget.Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
public class PartilharReservaActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_partilhar_reserva);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.partilharRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
|
if (back != null) {
|
||||||
|
back.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.firebase.storage.FirebaseStorage;
|
||||||
|
import com.google.firebase.storage.StorageReference;
|
||||||
|
import java.util.UUID;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ProfileDashboardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private EditText inputName, inputPhone, inputEmailEdit;
|
||||||
|
private String email, documentId, photoUrl;
|
||||||
|
private DatabaseReference databaseReference;
|
||||||
|
private ImageView imgProfile;
|
||||||
|
private androidx.activity.result.ActivityResultLauncher<Intent> imagePickerLauncher;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_profile_dashboard);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.profileRoot), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
String currentName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
||||||
|
|
||||||
|
if (email != null) {
|
||||||
|
documentId = email.replace(".", "_").replace("@", "_at_");
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseReference = FirebaseDatabase.getInstance().getReference().child("users");
|
||||||
|
|
||||||
|
inputName = findViewById(R.id.inputProfileName);
|
||||||
|
inputPhone = findViewById(R.id.inputProfilePhone);
|
||||||
|
inputEmailEdit = findViewById(R.id.inputProfileEmail);
|
||||||
|
imgProfile = findViewById(R.id.imgProfile);
|
||||||
|
|
||||||
|
if (currentName != null) {
|
||||||
|
inputName.setText(currentName);
|
||||||
|
}
|
||||||
|
if (email != null) {
|
||||||
|
inputEmailEdit.setText(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAdditionalProfileData();
|
||||||
|
|
||||||
|
imagePickerLauncher = registerForActivityResult(
|
||||||
|
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
||||||
|
android.net.Uri imageUri = result.getData().getData();
|
||||||
|
if (imageUri != null) {
|
||||||
|
uploadImageToFirebase(imageUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.cardProfileBig).setOnClickListener(v -> {
|
||||||
|
String[] options = {"Galeria", "URL da Imagem"};
|
||||||
|
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
|
||||||
|
builder.setTitle("Escolher Foto de Perfil");
|
||||||
|
builder.setItems(options, (dialog, which) -> {
|
||||||
|
if (which == 0) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK);
|
||||||
|
intent.setType("image/*");
|
||||||
|
imagePickerLauncher.launch(intent);
|
||||||
|
} else {
|
||||||
|
showUrlInputDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
Button btnSave = findViewById(R.id.btnSaveProfile);
|
||||||
|
Button btnBack = findViewById(R.id.btnVoltar);
|
||||||
|
Button btnFavs = findViewById(R.id.btnFavoritos);
|
||||||
|
Button btnRes = findViewById(R.id.btnMinhasReservas);
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(v -> finish());
|
||||||
|
btnSave.setOnClickListener(v -> saveProfile());
|
||||||
|
|
||||||
|
btnFavs.setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(this, FavoritosActivity.class));
|
||||||
|
});
|
||||||
|
|
||||||
|
btnRes.setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(this, MinhasReservasActivity.class));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showUrlInputDialog() {
|
||||||
|
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
|
||||||
|
builder.setTitle("Inserir URL da Imagem");
|
||||||
|
|
||||||
|
final EditText input = new EditText(this);
|
||||||
|
input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_URI);
|
||||||
|
input.setHint("https://exemplo.com/foto.jpg");
|
||||||
|
builder.setView(input);
|
||||||
|
|
||||||
|
builder.setPositiveButton("Confirmar", (dialog, which) -> {
|
||||||
|
String url = input.getText().toString().trim();
|
||||||
|
if (!TextUtils.isEmpty(url)) {
|
||||||
|
this.photoUrl = url;
|
||||||
|
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
|
Toast.makeText(this, "URL definida!", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("Cancelar", (dialog, which) -> dialog.cancel());
|
||||||
|
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchAdditionalProfileData() {
|
||||||
|
if (documentId == null)
|
||||||
|
return;
|
||||||
|
databaseReference.child(documentId).get().addOnSuccessListener(snapshot -> {
|
||||||
|
if (snapshot.exists()) {
|
||||||
|
String phone = snapshot.child("ownerPhone").getValue(String.class);
|
||||||
|
if (phone == null)
|
||||||
|
phone = snapshot.child("phone").getValue(String.class);
|
||||||
|
if (phone != null)
|
||||||
|
inputPhone.setText(phone);
|
||||||
|
|
||||||
|
String dbEmail = snapshot.child("email").getValue(String.class);
|
||||||
|
if (dbEmail != null)
|
||||||
|
inputEmailEdit.setText(dbEmail);
|
||||||
|
|
||||||
|
photoUrl = snapshot.child("photoUrl").getValue(String.class);
|
||||||
|
if (photoUrl != null && !photoUrl.isEmpty()) {
|
||||||
|
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadImageToFirebase(Uri imageUri) {
|
||||||
|
if (documentId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StorageReference storageRef = FirebaseStorage.getInstance().getReference()
|
||||||
|
.child("profile_pics/" + UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
||||||
|
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
||||||
|
photoUrl = uri.toString();
|
||||||
|
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
|
|
||||||
|
// Salvar a nova URL da foto imediatamente na DB
|
||||||
|
Map<String, Object> photoUpdate = new HashMap<>();
|
||||||
|
photoUpdate.put("photoUrl", photoUrl);
|
||||||
|
databaseReference.child(documentId).updateChildren(photoUpdate).addOnCompleteListener(dbTask -> {
|
||||||
|
if (dbTask.isSuccessful()) {
|
||||||
|
Toast.makeText(this, "Foto atualizada com sucesso!", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Foto enviada, mas não foi possível atualizar o perfil.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).addOnFailureListener(e -> {
|
||||||
|
Toast.makeText(this, "Falha no upload: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
android.util.Log.e("ProfileUpload", "Upload failed", e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveProfile() {
|
||||||
|
if (documentId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String newName = inputName.getText().toString().trim();
|
||||||
|
String newPhone = inputPhone.getText().toString().trim();
|
||||||
|
String newEmail = inputEmailEdit.getText().toString().trim();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(newName)) {
|
||||||
|
Toast.makeText(this, "Indique um nome.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("displayName", newName);
|
||||||
|
updates.put("phone", newPhone);
|
||||||
|
updates.put("email", newEmail);
|
||||||
|
if (photoUrl != null) {
|
||||||
|
updates.put("photoUrl", photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseReference.child(documentId).updateChildren(updates)
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
Toast.makeText(this, "Perfil atualizado!", Toast.LENGTH_SHORT).show();
|
||||||
|
Intent resultIntent = new Intent();
|
||||||
|
resultIntent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, newName);
|
||||||
|
setResult(RESULT_OK, resultIntent);
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.addOnFailureListener(
|
||||||
|
e -> Toast.makeText(this, "Falha ao atualizar perfil.", Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
|
}
|
||||||
103
app/src/main/java/com/example/pap_teste/ReservaAdapter.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
public interface OnReservaActionListener {
|
||||||
|
void onCheckIn(Reserva reserva);
|
||||||
|
|
||||||
|
void onCancel(Reserva reserva);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Reserva> reservas;
|
||||||
|
private final OnReservaActionListener listener;
|
||||||
|
|
||||||
|
public ReservaAdapter(List<Reserva> reservas, OnReservaActionListener listener) {
|
||||||
|
this.reservas = reservas;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_reserva_cliente, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
Reserva reserva = reservas.get(position);
|
||||||
|
holder.txtRestaurante.setText(reserva.getRestauranteName());
|
||||||
|
holder.txtDataHora.setText(reserva.getData() + " às " + reserva.getHora() + " • " + reserva.getPessoas() + "p");
|
||||||
|
holder.txtStatus.setText("Estado: " + reserva.getEstado());
|
||||||
|
|
||||||
|
// Enable check-in only if confirmed
|
||||||
|
boolean isConfirmed = "Confirmada".equals(reserva.getEstado());
|
||||||
|
boolean hasLocationPermission = androidx.core.app.ActivityCompat.checkSelfPermission(holder.itemView.getContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
|
||||||
|
if (isConfirmed) {
|
||||||
|
if (hasLocationPermission) {
|
||||||
|
holder.btnCheckIn.setEnabled(true);
|
||||||
|
if (holder.txtLocationWarning != null) {
|
||||||
|
holder.txtLocationWarning.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.btnCheckIn.setEnabled(false);
|
||||||
|
if (holder.txtLocationWarning != null) {
|
||||||
|
holder.txtLocationWarning.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
holder.btnCheckIn.setEnabled(false);
|
||||||
|
if (holder.txtLocationWarning != null) {
|
||||||
|
holder.txtLocationWarning.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show cancel only if not concluded or already cancelled/refused
|
||||||
|
boolean canCancel = "Pendente".equals(reserva.getEstado()) || "Confirmada".equals(reserva.getEstado());
|
||||||
|
holder.btnCancelar.setVisibility(canCancel ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
holder.btnCheckIn.setOnClickListener(v -> {
|
||||||
|
if (listener != null)
|
||||||
|
listener.onCheckIn(reserva);
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.btnCancelar.setOnClickListener(v -> {
|
||||||
|
if (listener != null)
|
||||||
|
listener.onCancel(reserva);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return reservas.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView txtRestaurante, txtDataHora, txtStatus, txtLocationWarning;
|
||||||
|
Button btnCheckIn, btnCancelar;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
txtRestaurante = itemView.findViewById(R.id.txtReservaRestaurante);
|
||||||
|
txtDataHora = itemView.findViewById(R.id.txtReservaDataHora);
|
||||||
|
txtStatus = itemView.findViewById(R.id.txtReservaStatus);
|
||||||
|
btnCheckIn = itemView.findViewById(R.id.btnCheckIn);
|
||||||
|
btnCancelar = itemView.findViewById(R.id.btnCancelar);
|
||||||
|
txtLocationWarning = itemView.findViewById(R.id.txtLocationWarning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ReservationOptionAdapter extends RecyclerView.Adapter<ReservationOptionAdapter.ViewHolder> {
|
||||||
|
private List<String> options;
|
||||||
|
private OnOptionClickListener listener;
|
||||||
|
|
||||||
|
public interface OnOptionClickListener {
|
||||||
|
void onOptionClick(String option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReservationOptionAdapter(List<String> options, OnOptionClickListener listener) {
|
||||||
|
this.options = options;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
String option = options.get(position);
|
||||||
|
holder.text1.setText(option);
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onOptionClick(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return options.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView text1;
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
text1 = itemView.findViewById(android.R.id.text1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
app/src/main/java/com/example/pap_teste/RestaurantAdapter.java
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.ValueEventListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RestaurantAdapter extends RecyclerView.Adapter<RestaurantAdapter.ViewHolder> {
|
||||||
|
private List<Restaurant> restaurants;
|
||||||
|
private OnRestaurantClickListener listener;
|
||||||
|
|
||||||
|
public interface OnRestaurantClickListener {
|
||||||
|
void onRestaurantClick(Restaurant restaurant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestaurantAdapter(List<Restaurant> restaurants, OnRestaurantClickListener listener) {
|
||||||
|
this.restaurants = restaurants;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_restaurant, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
Restaurant restaurant = restaurants.get(position);
|
||||||
|
holder.text1.setText(restaurant.getName());
|
||||||
|
holder.text2.setText(restaurant.getCategory() + (restaurant.isAvailable() ? " - Disponível" : " - Indisponível"));
|
||||||
|
|
||||||
|
if (restaurant.getLogoUrl() != null && !restaurant.getLogoUrl().isEmpty()) {
|
||||||
|
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||||
|
.load(restaurant.getLogoUrl())
|
||||||
|
.circleCrop()
|
||||||
|
.into(holder.imgThumb);
|
||||||
|
} else {
|
||||||
|
holder.imgThumb.setImageResource(R.mipmap.ic_launcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onRestaurantClick(restaurant);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
if (user != null && user.getEmail() != null && restaurant.getEmail() != null) {
|
||||||
|
String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
String encodedRestEmail = restaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||||
|
DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("users")
|
||||||
|
.child(encodedUserEmail).child("favorites").child(encodedRestEmail);
|
||||||
|
|
||||||
|
favRef.addValueEventListener(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
if (snapshot.exists()) {
|
||||||
|
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||||
|
} else {
|
||||||
|
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) { }
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.btnFavorite.setOnClickListener(v -> {
|
||||||
|
favRef.addListenerForSingleValueEvent(new ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||||
|
if (snapshot.exists()) {
|
||||||
|
favRef.removeValue();
|
||||||
|
} else {
|
||||||
|
favRef.setValue(restaurant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) { }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return restaurants.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView text1, text2;
|
||||||
|
ImageButton btnFavorite;
|
||||||
|
android.widget.ImageView imgThumb;
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
text1 = itemView.findViewById(R.id.txtRestaurantName);
|
||||||
|
text2 = itemView.findViewById(R.id.txtRestaurantCategory);
|
||||||
|
btnFavorite = itemView.findViewById(R.id.btnFavorite);
|
||||||
|
imgThumb = itemView.findViewById(R.id.imgRestaurantThumb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
public class FoodCategory {
|
||||||
|
private String name;
|
||||||
|
private int imageResId;
|
||||||
|
|
||||||
|
public FoodCategory(String name, int imageResId) {
|
||||||
|
this.name = name;
|
||||||
|
this.imageResId = imageResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageResId() {
|
||||||
|
return imageResId;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/src/main/java/com/example/pap_teste/models/Mesa.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
public class Mesa {
|
||||||
|
private String id;
|
||||||
|
private int numero;
|
||||||
|
private int capacidade;
|
||||||
|
private String estado;
|
||||||
|
private String restauranteEmail;
|
||||||
|
|
||||||
|
public Mesa() {
|
||||||
|
// Default constructor required for calls to DataSnapshot.getValue(Mesa.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mesa(String id, int numero, int capacidade, String estado, String restauranteEmail) {
|
||||||
|
this.id = id;
|
||||||
|
this.numero = numero;
|
||||||
|
this.capacidade = capacidade;
|
||||||
|
this.estado = estado;
|
||||||
|
this.restauranteEmail = restauranteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumero() {
|
||||||
|
return numero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumero(int numero) {
|
||||||
|
this.numero = numero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCapacidade() {
|
||||||
|
return capacidade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCapacidade(int capacidade) {
|
||||||
|
this.capacidade = capacidade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEstado() {
|
||||||
|
return estado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEstado(String estado) {
|
||||||
|
this.estado = estado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRestauranteEmail() {
|
||||||
|
return restauranteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRestauranteEmail(String restauranteEmail) {
|
||||||
|
this.restauranteEmail = restauranteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Mesa " + numero;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
app/src/main/java/com/example/pap_teste/models/Reserva.java
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
public class Reserva {
|
||||||
|
private String id;
|
||||||
|
private String clienteEmail;
|
||||||
|
private String restauranteName;
|
||||||
|
private String restauranteEmail;
|
||||||
|
private String data;
|
||||||
|
private String hora;
|
||||||
|
private int pessoas;
|
||||||
|
private String estado; // Pendente, Confirmada, Concluída, Cancelada, Recusada
|
||||||
|
|
||||||
|
public Reserva() {
|
||||||
|
// Required for Firebase
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reserva(String id, String clienteEmail, String restauranteName, String restauranteEmail, String data,
|
||||||
|
String hora, int pessoas, String estado) {
|
||||||
|
this.id = id;
|
||||||
|
this.clienteEmail = clienteEmail;
|
||||||
|
this.restauranteName = restauranteName;
|
||||||
|
this.restauranteEmail = restauranteEmail;
|
||||||
|
this.data = data;
|
||||||
|
this.hora = hora;
|
||||||
|
this.pessoas = pessoas;
|
||||||
|
this.estado = estado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClienteEmail() {
|
||||||
|
return clienteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClienteEmail(String clienteEmail) {
|
||||||
|
this.clienteEmail = clienteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRestauranteName() {
|
||||||
|
return restauranteName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRestauranteName(String restauranteName) {
|
||||||
|
this.restauranteName = restauranteName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRestauranteEmail() {
|
||||||
|
return restauranteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRestauranteEmail(String restauranteEmail) {
|
||||||
|
this.restauranteEmail = restauranteEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(String data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHora() {
|
||||||
|
return hora;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHora(String hora) {
|
||||||
|
this.hora = hora;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPessoas() {
|
||||||
|
return pessoas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPessoas(int pessoas) {
|
||||||
|
this.pessoas = pessoas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEstado() {
|
||||||
|
return estado;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEstado(String estado) {
|
||||||
|
this.estado = estado;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
public class Restaurant {
|
||||||
|
private String name;
|
||||||
|
private String category;
|
||||||
|
private String email;
|
||||||
|
private boolean available;
|
||||||
|
private String logoUrl;
|
||||||
|
|
||||||
|
// No-argument constructor required for Firebase
|
||||||
|
public Restaurant() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Restaurant(String name, String category, String email, boolean available) {
|
||||||
|
this.name = name;
|
||||||
|
this.category = category;
|
||||||
|
this.email = email;
|
||||||
|
this.available = available;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Restaurant(String name, String category, String email, boolean available, String logoUrl) {
|
||||||
|
this.name = name;
|
||||||
|
this.category = category;
|
||||||
|
this.email = email;
|
||||||
|
this.available = available;
|
||||||
|
this.logoUrl = logoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public String getCategory() { return category; }
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public boolean isAvailable() { return available; }
|
||||||
|
public String getLogoUrl() { return logoUrl; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public void setCategory(String category) { this.category = category; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
public void setAvailable(boolean available) { this.available = available; }
|
||||||
|
public void setLogoUrl(String logoUrl) { this.logoUrl = logoUrl; }
|
||||||
|
}
|
||||||
51
app/src/main/java/com/example/pap_teste/models/Staff.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
public class Staff {
|
||||||
|
private String name;
|
||||||
|
private String zona;
|
||||||
|
private String mesa;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
public Staff(String name, String zona, String mesa, String id) {
|
||||||
|
this.name = name;
|
||||||
|
this.zona = zona;
|
||||||
|
this.mesa = mesa;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Staff() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZona() {
|
||||||
|
return zona;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZona(String zona) {
|
||||||
|
this.zona = zona;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMesa() {
|
||||||
|
return mesa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMesa(String mesa) {
|
||||||
|
this.mesa = mesa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
app/src/main/res/drawable/bg_tabs.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#DDDDDD" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
</shape>
|
||||||
|
|
||||||
|
</selector>
|
||||||
5
app/src/main/res/drawable/btn_light.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#EDEDED" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
6
app/src/main/res/drawable/btn_light_border.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<stroke android:width="2dp" android:color="#888" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
30
app/src/main/res/drawable/btn_primary.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#2200001A">
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="#00001A" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<padding
|
||||||
|
android:left="12dp"
|
||||||
|
android:top="8dp"
|
||||||
|
android:right="12dp"
|
||||||
|
android:bottom="8dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
app/src/main/res/drawable/cat_carnes.png
Normal file
|
After Width: | Height: | Size: 526 KiB |
BIN
app/src/main/res/drawable/cat_massas.png
Normal file
|
After Width: | Height: | Size: 485 KiB |
BIN
app/src/main/res/drawable/cat_pizzas.png
Normal file
|
After Width: | Height: | Size: 732 KiB |
BIN
app/src/main/res/drawable/cat_sobremesas.png
Normal file
|
After Width: | Height: | Size: 548 KiB |
BIN
app/src/main/res/drawable/cat_sushi.png
Normal file
|
After Width: | Height: | Size: 478 KiB |
8
app/src/main/res/drawable/circle_bg.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#000" />
|
||||||
|
</shape>
|
||||||
|
|
||||||
|
</selector>
|
||||||
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
30
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_visibility.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 5.99,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_visibility_off.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 5.99,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
|
||||||
|
</vector>
|
||||||
7
app/src/main/res/drawable/input_bg.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#EFEFEF" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
|
</selector>
|
||||||
BIN
app/src/main/res/drawable/na_mesa.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
5
app/src/main/res/drawable/tab_selected.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#8BC34A" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
5
app/src/main/res/drawable/tab_unselected.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
123
app/src/main/res/layout/activity_account_created.xml
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/accountCreatedRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".AccountCreatedActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardAccountCreated"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="6dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtCongrats"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Conta criada!"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="26sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtWelcome"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Nome"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtEmail"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="email@example.com"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:background="@color/colorDivider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtRoleLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Função atribuída:"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtRole"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="ADMIN"
|
||||||
|
android:textColor="@color/colorSuccess"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtInstructions"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Vamos configurar o seu espaço. Pode aceder à sua área agora."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:lineSpacingExtra="4dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnContinuar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Ir para a minha área"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
164
app/src/main/res/layout/activity_add_staff.xml
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".AddStaffActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltarAddStaff"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtAdicionarTitulo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Novo Funcionário"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltarAddStaff" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtAdicionarDesc"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Introduza os dados e atribuições deste membro:"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtAdicionarTitulo" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtAdicionarDesc">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Nome do Funcionário"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/nammeEditText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Ex: João Silva"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Zona de Trabalho"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/zoneLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/zonaSpinner"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/input_bg" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAddZone"
|
||||||
|
android:layout_width="54dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:text="+"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Mesa a Atribuir"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/mesaLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/mesaSpinner"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:background="@drawable/input_bg" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/addButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="36dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Adicionar Membro"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
126
app/src/main/res/layout/activity_checkin_antecipado.xml
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/checkinRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".CheckInAntecipadoActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloCheckin"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Check-in antecipado"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDescricaoCheckin"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Aqui poderá confirmar a sua chegada ao restaurante."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloCheckin"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDistancia"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:text="A calcular distância..."
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtDescricaoCheckin"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtStatusDistancia"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Por favor, aguarde enquanto validamos a sua localização."
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtDistancia"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtEnderecoRestaurante"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="A carregar morada..."
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtStatusDistancia"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAbrirMapa"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Ver no Mapa"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtEnderecoRestaurante"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmarChegada"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorSuccess"
|
||||||
|
android:text="Confirmar Chegada"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:enabled="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnAbrirMapa"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
290
app/src/main/res/layout/activity_client_dashboard.xml
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/clientRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".ClientDashboardActivity">
|
||||||
|
|
||||||
|
<!-- Top Bar with Profile Icon -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardProfile"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cardCornerRadius="25dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgProfile"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/circle_bg" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtClientGreeting"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:text="Olá, convidado"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/cardProfile"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/cardProfile" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="Sair"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/cardProfile" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/clientScroll"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cardProfile">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="40dp">
|
||||||
|
|
||||||
|
<!-- Categories Section -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="Categorias"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvCategories"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_food_category" />
|
||||||
|
|
||||||
|
<!-- Integrated Next Reservation Card -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:text="A sua próxima reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardNextReservation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="24dp"
|
||||||
|
app:cardElevation="6dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtResTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sabor & Arte"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtResTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/txtResTitle"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="Amanhã às 20h00 • 2 pessoas"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgResIcon"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:src="@drawable/circle_bg"
|
||||||
|
app:tint="@color/colorPrimary" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginVertical="20dp"
|
||||||
|
android:background="@color/colorDivider" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCheckIn"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Check-in"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnPartilhar"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Partilhar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Actions Section -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:text="Mais opções"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:columnCount="1">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardNewRes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface">
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/btnNovaReserva"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Reservar"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardMinhasReservas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface">
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/btnMinhasReservas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Minhas Reservas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
|
<!-- Status Section -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtClientStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:text="Tudo pronto para a sua próxima refeição!"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtClientRole"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Modo Cliente"
|
||||||
|
android:textColor="#94A3B8"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
176
app/src/main/res/layout/activity_definicoes_admin.xml
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/definicoesRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".DefinicoesAdminActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="48dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Definições do Estabelecimento"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
|
||||||
|
<!-- Improved Logo using MaterialCardView -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cardCornerRadius="60dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgRestaurantLogo"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/circle_bg" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnChangeLogo"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="Alterar Logótipo"
|
||||||
|
android:textAllCaps="false" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Configure o raio de segurança para check-in antecipado e regras associadas."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Raio de Segurança (metros)"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputRadius"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:hint="Ex: 500"
|
||||||
|
android:inputType="number"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Informação do Restaurante"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputAddress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:hint="Morada (Ex: Rua Estreita, 12)"
|
||||||
|
android:inputType="textPostalAddress"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Categoria"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerCategory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/input_bg" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSaveSettings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Guardar Definições"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
183
app/src/main/res/layout/activity_detalhes_reservas.xml
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/detalhesReservasRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".DetalhesReservasActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloDetalhes"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Detalhes das reservas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="26sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDescricaoDetalhes"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Selecione uma reserva para ver os detalhes e atualizar o estado."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloDetalhes" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/listReservas"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:divider="@color/colorDivider"
|
||||||
|
android:dividerHeight="1dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtDescricaoDetalhes"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/cardDetalhes"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardDetalhes"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="10dp"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Reserva selecionada"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaInfo"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="Selecione uma reserva"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaNotas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text=""
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaEstado"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Estado:"
|
||||||
|
android:textColor="@color/colorSuccess"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmarReserva"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Confirmar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCancelarReserva"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginHorizontal="4dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Recusar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnApagarReserva"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:strokeColor="@color/colorError"
|
||||||
|
android:text="Apagar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMensagemReserva"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text=""
|
||||||
|
android:textColor="@color/colorSuccess"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
329
app/src/main/res/layout/activity_establishment_dashboard.xml
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/establishmentRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".EstablishmentDashboardActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtEstabTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Estabelecimento"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtEstabSubtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Veja as reservas ao vivo e controle a ocupação."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:lineSpacingExtra="2dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtEstabRole"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Função: ADMIN"
|
||||||
|
android:textColor="@color/colorSuccess"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardReservasHoje"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Reservas"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservasHojeDash"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="00"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Mesas"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMesasLivresDash"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="00"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Espera"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtListaEsperaDash"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="00"
|
||||||
|
android:textColor="@color/colorWarning"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:text="Próximas reservas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="4dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llProximasReservas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="A procurar próximas reservas..."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:text="Operações rápidas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAbrirEspera"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Abrir lista de espera"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnGerirMesas"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Editar mesas"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnDetalhesReservas"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Detalhes das reservas"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnGestaoStaff"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Gestão de staff"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnDefinicoes"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Definições"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorWarning"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Alertas operacionais"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="• Mesa 07 aguarda confirmação há 10 min\n• 2 clientes aguardam resposta no chat"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
198
app/src/main/res/layout/activity_explorar_restaurantes.xml
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/explorarRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".ExplorarRestaurantesActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloExplorar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Explorar restaurantes"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvRestaurants"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloExplorar"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scrollReservaDetails"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloExplorar">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="48dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVerAvaliacoes"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Avaliações"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
app:strokeColor="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnUploadFoto"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Adicionar Foto"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError"
|
||||||
|
app:strokeColor="@color/colorError" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Data da reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSelectDate"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Selecionar Data"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Hora da reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSelectTime"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Selecionar Hora"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Número de pessoas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPartySize"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:inputType="number"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:hint="Ex: 2"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmarReserva"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Confirmar Reserva"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
52
app/src/main/res/layout/activity_favoritos.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/favoritosRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".FavoritosActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloFavoritos"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Restaurantes favoritos"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvFavoritos"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloFavoritos"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
172
app/src/main/res/layout/activity_gerir_mesas.xml
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/gerirMesasRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".GerirMesasActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloGerirMesas"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Editar mesas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDescricaoGerirMesas"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Atualize rapidamente as mesas disponíveis, capacidade e estado."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloGerirMesas" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/formMesas"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtDescricaoGerirMesas">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Editar ou adicionar mesa"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputMesaNumero"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Número da mesa"
|
||||||
|
android:inputType="number"
|
||||||
|
android:paddingHorizontal="16dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputMesaCapacidade"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Capacidade (lugares)"
|
||||||
|
android:inputType="number"
|
||||||
|
android:paddingHorizontal="16dp" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerEstadoMesa"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginTop="12dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnGuardarMesa"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Guardar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnRemoverMesa"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:strokeColor="@color/colorError"
|
||||||
|
android:text="Remover"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMensagemMesa"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text=""
|
||||||
|
android:textColor="@color/colorSuccess"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtListaTitulo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Mesas registadas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/formMesas" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/listMesas"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:divider="@color/colorDivider"
|
||||||
|
android:dividerHeight="1dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtListaTitulo"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
188
app/src/main/res/layout/activity_gestao_staff.xml
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/gestaoStaffRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".GestaoStaffActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloGestaoStaff"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Gestão de staff"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDescricaoGestaoStaff"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Zona para configurar turnos, equipas e disponibilidade do staff."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloGestaoStaff"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/formStaff"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtDescricaoGestaoStaff">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Atribuir funcionário a uma mesa"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerNomeStaff"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:padding="0dp" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerMesaStaff"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:padding="0dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnAtribuirStaff"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Guardar atribuição"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnEliminarStaff"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Eliminar Funcionário"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMensagemStaff"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text=""
|
||||||
|
android:textColor="@color/colorSuccess"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtListaStaffTitulo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Funcionários e mesas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/formStaff" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnGerirMesasStaff"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Gerir Mesas"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/txtListaStaffTitulo"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/txtListaStaffTitulo" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/listStaffMesas"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:divider="@color/colorDivider"
|
||||||
|
android:dividerHeight="1dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtListaStaffTitulo"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/floatingActionButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:clickable="true"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
app:tint="@color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:srcCompat="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
159
app/src/main/res/layout/activity_lista_espera.xml
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/listaEsperaRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".ListaEsperaActivity">
|
||||||
|
|
||||||
|
<!-- Header Section -->
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloListaEspera"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Lista de espera"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="24dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDescricaoListaEspera"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Aqui o estabelecimento irá gerir a lista de espera em tempo real."
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloListaEspera"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<!-- List Section -->
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/listReservasP"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:divider="@color/colorDivider"
|
||||||
|
android:dividerHeight="1dp"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtDescricaoListaEspera"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/detalhesCardP" />
|
||||||
|
|
||||||
|
<!-- Selection/Details Card Bottom -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/detalhesCardP"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="12dp"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="#E2E8F0"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaInfoP"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Selecione uma reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaNotasP"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text=""
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:lineSpacingExtra="2dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMensagemReservaP"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text=""
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="italic" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="2">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnRecusarReservaP"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="#FEE2E2"
|
||||||
|
android:text="Recusar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmarReservaP"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorSuccess"
|
||||||
|
android:text="Aceitar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
259
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/logoNaMesa"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:src="@drawable/na_mesa"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginTop="50dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="NaMesa"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/logoNaMesa" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtSub"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="Sistema de Reserva de Mesas"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTitle" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:layout_marginBottom="40dp"
|
||||||
|
app:cardCornerRadius="24dp"
|
||||||
|
app:cardElevation="8dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtSub"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.0">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@drawable/bg_tabs"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="3dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnCliente"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/btn_light"
|
||||||
|
android:text="Cliente"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="#231F1F"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnEstabelecimento"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:text="Estabelecimento"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/bg_tabs"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="3dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnEntrar"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/tab_selected"
|
||||||
|
android:text="Entrar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="#231F1F"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnCriarConta"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:text="Criar Conta"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="O seu nome"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputEmail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Email"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:paddingHorizontal="16dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputOwnerPhone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Telefone do proprietário"
|
||||||
|
android:inputType="phone"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputEstablishmentName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Nome do estabelecimento"
|
||||||
|
android:inputType="textCapWords"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputEstablishmentEmail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Email do estabelecimento"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputEstablishmentPhone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Telefone do estabelecimento"
|
||||||
|
android:inputType="phone"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/inputPassword"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:hint="Palavra-passe"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="48dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iconPasswordVisibility"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:src="@drawable/ic_visibility_off"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtForgotPassword"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:text="Esqueceu-se da palavra-passe?"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"/>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnFinalCriarConta"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Entrar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
51
app/src/main/res/layout/activity_minhas_reservas.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/minhasReservasRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".MinhasReservasActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloMinhasReservas"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Minhas Reservas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvMinhasReservas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloMinhasReservas"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
180
app/src/main/res/layout/activity_nova_reserva.xml
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/novaReservaRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".NovaReservaActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloNovaReserva"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Nova reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvCategories"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloNovaReserva"
|
||||||
|
tools:listitem="@layout/item_food_category" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvRestaurants"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloNovaReserva"
|
||||||
|
tools:listitem="@layout/item_restaurant" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scrollNovaReserva"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:scrollbars="none"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloNovaReserva">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="48dp">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Data da reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSelectDate"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Selecionar Data"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Hora da reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSelectTime"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
android:text="Selecionar Hora"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Número de pessoas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPartySize"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:inputType="number"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:hint="Ex: 2"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmarReserva"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Confirmar Reserva"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
52
app/src/main/res/layout/activity_partilhar_reserva.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/partilharRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".PartilharReservaActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTituloPartilhar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Partilhar reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDescricaoPartilhar"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Ecrã onde o cliente poderá enviar convites/links de partilha da reserva."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtTituloPartilhar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
193
app/src/main/res/layout/activity_profile_dashboard.xml
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/profileRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorBackground"
|
||||||
|
tools:context=".ProfileDashboardActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnVoltar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="← Voltar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtProfileTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="O seu Perfil"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardProfileBig"
|
||||||
|
android:layout_width="110dp"
|
||||||
|
android:layout_height="110dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cardCornerRadius="55dp"
|
||||||
|
app:cardElevation="6dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtProfileTitle"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgProfile"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/circle_bg" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cardProfileBig"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="32dp"
|
||||||
|
android:paddingBottom="40dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Nome de Exibição"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
app:boxCornerRadiusTopStart="12dp"
|
||||||
|
app:boxCornerRadiusTopEnd="12dp"
|
||||||
|
app:boxCornerRadiusBottomStart="12dp"
|
||||||
|
app:boxCornerRadiusBottomEnd="12dp"
|
||||||
|
app:boxStrokeWidth="0dp"
|
||||||
|
app:boxBackgroundColor="@color/colorSurface">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/inputProfileName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:hint="O seu nome" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Telemóvel"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
app:boxCornerRadiusTopStart="12dp"
|
||||||
|
app:boxCornerRadiusTopEnd="12dp"
|
||||||
|
app:boxCornerRadiusBottomStart="12dp"
|
||||||
|
app:boxCornerRadiusBottomEnd="12dp"
|
||||||
|
app:boxStrokeWidth="0dp"
|
||||||
|
app:boxBackgroundColor="@color/colorSurface">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/inputProfilePhone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:hint="O seu número"
|
||||||
|
android:inputType="phone"
|
||||||
|
android:paddingHorizontal="16dp" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Email"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
app:boxCornerRadiusTopStart="12dp"
|
||||||
|
app:boxCornerRadiusTopEnd="12dp"
|
||||||
|
app:boxCornerRadiusBottomStart="12dp"
|
||||||
|
app:boxCornerRadiusBottomEnd="12dp"
|
||||||
|
app:boxStrokeWidth="0dp"
|
||||||
|
app:boxBackgroundColor="@color/colorSurface">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/inputProfileEmail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:hint="O seu email"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:paddingHorizontal="16dp" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSaveProfile"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Guardar Alterações"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginVertical="32dp"
|
||||||
|
android:background="@color/colorDivider" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnFavoritos"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Os meus Favoritos"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnMinhasReservas"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
app:cornerRadius="14dp"
|
||||||
|
android:text="Histórico de Reservas"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
48
app/src/main/res/layout/item_food_category.xml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtCategoryName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@id/cvImageCategory"
|
||||||
|
android:text="Categoria"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cvImageCategory"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="0dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgCategory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/ic_launcher_background" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</RelativeLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
91
app/src/main/res/layout/item_reserva_cliente.xml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaRestaurante"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Nome do Restaurante"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaDataHora"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Hoje às 20:00"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservaStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Estado: Pendente"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/colorSecondary"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCheckIn"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:text="Check-in"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCancelar"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
app:strokeColor="@color/colorError"
|
||||||
|
android:text="Cancelar"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/colorError" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtLocationWarning"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Dê permissões de localização para aceder ao check-in."
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorWarning"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
68
app/src/main/res/layout/item_restaurant.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:strokeWidth="1dp"
|
||||||
|
app:strokeColor="@color/colorDivider">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/imgRestaurantThumb"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@mipmap/ic_launcher"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Medium" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toEndOf="@id/imgRestaurantThumb"
|
||||||
|
android:layout_toStartOf="@id/btnFavorite"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtRestaurantName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Restaurant Name"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtRestaurantCategory"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Category"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnFavorite"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@android:drawable/btn_star_big_off" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
7
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Base.Theme.Pap_teste" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your dark theme here. -->
|
||||||
|
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
24
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Brand Colors -->
|
||||||
|
<color name="colorPrimary">#06C167</color>
|
||||||
|
<color name="colorPrimaryVariant">#05A357</color>
|
||||||
|
<color name="colorSecondary">#06C167</color>
|
||||||
|
|
||||||
|
<!-- Neutral Colors -->
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
<color name="colorBackground">#F8FAFC</color>
|
||||||
|
<color name="colorSurface">#FFFFFF</color>
|
||||||
|
<color name="colorTextPrimary">#1E293B</color>
|
||||||
|
<color name="colorTextSecondary">#64748B</color>
|
||||||
|
|
||||||
|
<!-- Semantic Colors -->
|
||||||
|
<color name="colorSuccess">#10B981</color>
|
||||||
|
<color name="colorError">#EF4444</color>
|
||||||
|
<color name="colorWarning">#F59E0B</color>
|
||||||
|
<color name="colorDivider">#E2E8F0</color>
|
||||||
|
|
||||||
|
<!-- Legacy compatibility -->
|
||||||
|
<color name="my_light_primary">#BE1F13</color>
|
||||||
|
</resources>
|
||||||