Compare commits
11 Commits
main
...
b2bb591bb7
| Author | SHA1 | Date | |
|---|---|---|---|
| b2bb591bb7 | |||
| 585af79ffa | |||
| 9d57ed4d0a | |||
| 824ff28001 | |||
| 86dbe2d319 | |||
| 8c1e01dc4c | |||
| 995d23ac7a | |||
| eaa3d86fc9 | |||
| d192568ed8 | |||
| 31a7cbb2df | |||
| fe9266263f |
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-04-14T16:06:26.670067Z">
|
||||
<DropdownSelection timestamp="2026-06-30T16:03:11.250011Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=b93659d0e5dd" />
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230409/.android/avd/Pixel_8_Pro.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
BIN
OperatingHoursDay.class
Normal file
BIN
OperatingHoursDay.class
Normal file
Binary file not shown.
BIN
TestFirebase.class
Normal file
BIN
TestFirebase.class
Normal file
Binary file not shown.
17
TestFirebase.java
Normal file
17
TestFirebase.java
Normal file
@@ -0,0 +1,17 @@
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
public class TestFirebase {
|
||||
public static void main(String[] args) throws Exception {
|
||||
PropertyDescriptor[] pds = Introspector.getBeanInfo(OperatingHoursDay.class).getPropertyDescriptors();
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
System.out.println("Property: " + pd.getName() + ", readMethod: " + (pd.getReadMethod() != null ? pd.getReadMethod().getName() : "null"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OperatingHoursDay {
|
||||
private boolean isOpen;
|
||||
public boolean getIsOpen() { return isOpen; }
|
||||
public void setIsOpen(boolean isOpen) { this.isOpen = isOpen; }
|
||||
}
|
||||
@@ -10,7 +10,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId = "com.example.pap_teste"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
@@ -44,11 +44,6 @@ dependencies {
|
||||
implementation(libs.firebase.database)
|
||||
implementation(libs.glide)
|
||||
implementation(libs.firebase.storage)
|
||||
|
||||
// Room
|
||||
implementation(libs.room.runtime)
|
||||
annotationProcessor(libs.room.compiler)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
<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" />
|
||||
|
||||
<!-- Notification permissions -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -24,72 +28,100 @@
|
||||
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=".SplashActivity"
|
||||
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>
|
||||
|
||||
<activity
|
||||
android:name=".OnboardingActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ClientDashboardActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".RestaurantDetailActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ReservationActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".FavoritosActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".EstablishmentDashboardActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".AccountCreatedActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".NovaReservaActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".CheckInAntecipadoActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MinhasReservasActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".PartilharReservaActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ProfileDashboardActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ExplorarRestaurantesActivity"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".ReservationNotificationService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
135
app/src/main/java/com/example/pap_teste/AddStaffActivity.java
Normal file
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@@ -22,9 +21,13 @@ import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
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 {
|
||||
@@ -32,10 +35,10 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
||||
private TextView txtDistancia, txtStatus, txtEndereco;
|
||||
private Button btnConfirmarChegada, btnAbrirMapa;
|
||||
private LocationManager locationManager;
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
private DatabaseReference databaseReference;
|
||||
|
||||
private double restaurantLat, restaurantLon;
|
||||
private int securityDistance = 500;
|
||||
private int securityDistance = 500; // default 500m
|
||||
private boolean settingsLoaded = false;
|
||||
private String restaurantAddress;
|
||||
|
||||
@@ -44,27 +47,27 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_checkin_antecipado);
|
||||
|
||||
View root = findViewById(R.id.checkinRoot);
|
||||
if (root != null) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
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);
|
||||
|
||||
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||
if (back != null) {
|
||||
back.setOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
String restaurantEmail = getIntent().getStringExtra("restaurant_email");
|
||||
if (restaurantEmail != null) {
|
||||
loadRestaurantSettings(restaurantEmail);
|
||||
String restaurantId = restaurantEmail.replace(".", "_").replace("@", "_at_");
|
||||
loadRestaurantSettings(restaurantId);
|
||||
} else {
|
||||
txtStatus.setText("Erro: Restaurante não identificado.");
|
||||
}
|
||||
@@ -73,11 +76,11 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
||||
if (hasRequiredPermissions()) {
|
||||
startLocationUpdates();
|
||||
} else {
|
||||
txtStatus.setText("Permissões de localização necessárias.");
|
||||
txtStatus.setText("Permissões em falta para localização/proximidade.");
|
||||
}
|
||||
|
||||
btnConfirmarChegada.setOnClickListener(v -> {
|
||||
Toast.makeText(this, "Check-in realizado com sucesso!", Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(this, "Chegada confirmada com sucesso!", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
});
|
||||
|
||||
@@ -95,36 +98,48 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
||||
}
|
||||
|
||||
private boolean hasRequiredPermissions() {
|
||||
boolean fineLocation = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||
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;
|
||||
boolean btScan = ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED;
|
||||
return fineLocation && btScan;
|
||||
}
|
||||
return fineLocation;
|
||||
}
|
||||
|
||||
private void loadRestaurantSettings(String email) {
|
||||
db.collection("Restaurantes").whereEqualTo("email", email).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
if (!queryDocumentSnapshots.isEmpty()) {
|
||||
QueryDocumentSnapshot doc = (QueryDocumentSnapshot) queryDocumentSnapshots.getDocuments().get(0);
|
||||
restaurantAddress = doc.getString("address");
|
||||
Long dist = doc.getLong("securityDistance");
|
||||
securityDistance = dist != null ? dist.intValue() : 500;
|
||||
private void loadRestaurantSettings(String restaurantId) {
|
||||
databaseReference = FirebaseDatabase.getInstance().getReference().child("Restaurantes").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 {
|
||||
Double lat = doc.getDouble("latitude");
|
||||
Double lon = doc.getDouble("longitude");
|
||||
if (lat != null && lon != null) {
|
||||
restaurantLat = lat;
|
||||
restaurantLon = lon;
|
||||
settingsLoaded = true;
|
||||
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.");
|
||||
}
|
||||
} else {
|
||||
txtStatus.setText("Definições do restaurante não encontradas.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
txtStatus.setText("Erro ao carregar definições do restaurante.");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -138,37 +153,47 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
||||
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 ignored) {}
|
||||
} 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;
|
||||
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.");
|
||||
txtStatus.setText("Erro ao iniciar localização: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(@NonNull Location location) {
|
||||
if (!settingsLoaded) return;
|
||||
if (!settingsLoaded)
|
||||
return;
|
||||
|
||||
float[] results = new float[1];
|
||||
Location.distanceBetween(location.getLatitude(), location.getLongitude(), restaurantLat, restaurantLon, results);
|
||||
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.");
|
||||
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("Fora do raio de segurança (%dm).", securityDistance));
|
||||
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);
|
||||
}
|
||||
@@ -177,10 +202,20 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (locationManager != null) locationManager.removeUpdates(this);
|
||||
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) {}
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(@NonNull String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(@NonNull String provider) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,276 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
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 ClientDashboardActivity extends AppCompatActivity {
|
||||
|
||||
private String email, displayName;
|
||||
private TextView txtGreeting;
|
||||
private ImageView imgProfile;
|
||||
private EditText etSearch;
|
||||
private RecyclerView rvFeatured;
|
||||
private ProgressBar progressBar;
|
||||
private View layoutFeatured;
|
||||
private android.widget.LinearLayout categoriesContainer;
|
||||
|
||||
private List<Restaurant> allRestaurants = new ArrayList<>();
|
||||
private List<Restaurant> filteredRestaurants = new ArrayList<>();
|
||||
|
||||
private FeaturedRestaurantAdapter featuredAdapter;
|
||||
|
||||
private final String[] CATEGORIES = { "Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas" };
|
||||
private String currentSearchFilter = "";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_client_dashboard_v2);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_client_dashboard);
|
||||
|
||||
BottomNavigationView bottomNav = findViewById(R.id.bottomNavigation);
|
||||
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;
|
||||
});
|
||||
|
||||
email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||
displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
||||
|
||||
initViews();
|
||||
setupBottomNavigation();
|
||||
setupSearch();
|
||||
|
||||
updateGreeting();
|
||||
fetchProfilePicture();
|
||||
fetchRestaurants();
|
||||
|
||||
// Initial fragment
|
||||
loadFragment(new HomeFragment());
|
||||
// Iniciar serviço de notificações se for Android O ou superior
|
||||
Intent serviceIntent = new Intent(this, ReservationNotificationService.class);
|
||||
startService(serviceIntent);
|
||||
}
|
||||
|
||||
private RestaurantAdapter.OnRestaurantClickListener clickListener;
|
||||
|
||||
private void initViews() {
|
||||
txtGreeting = findViewById(R.id.txtClientGreeting);
|
||||
imgProfile = findViewById(R.id.imgProfile);
|
||||
etSearch = findViewById(R.id.etSearch);
|
||||
rvFeatured = findViewById(R.id.rvFeatured);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
layoutFeatured = findViewById(R.id.layoutFeatured);
|
||||
categoriesContainer = findViewById(R.id.categoriesContainer);
|
||||
|
||||
rvFeatured.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||
|
||||
// Click listener for restaurants to open booking flow
|
||||
clickListener = restaurant -> {
|
||||
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
||||
intent.putExtra("restaurant", restaurant);
|
||||
startActivity(intent);
|
||||
};
|
||||
|
||||
featuredAdapter = new FeaturedRestaurantAdapter(new ArrayList<>(), clickListener);
|
||||
rvFeatured.setAdapter(featuredAdapter);
|
||||
|
||||
// Click listener for profile picture in the header
|
||||
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);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
private void setupBottomNavigation() {
|
||||
BottomNavigationView bottomNav = findViewById(R.id.bottomNavigation);
|
||||
bottomNav.setOnItemSelectedListener(item -> {
|
||||
Fragment fragment = null;
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == R.id.nav_home) {
|
||||
fragment = new HomeFragment();
|
||||
} else if (id == R.id.nav_reservations) {
|
||||
fragment = new MyReservationsFragment();
|
||||
} else if (id == R.id.nav_profile) {
|
||||
fragment = new ProfileFragment();
|
||||
}
|
||||
|
||||
if (fragment != null) {
|
||||
loadFragment(fragment);
|
||||
return true;
|
||||
} else if (id == R.id.nav_reservations) {
|
||||
Intent intent = new Intent(this, MinhasReservasActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, email);
|
||||
startActivity(intent);
|
||||
return false; // Don't highlight if we open new activity
|
||||
} else if (id == R.id.nav_profile) {
|
||||
Intent intent = new Intent(this, ProfileDashboardActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, email);
|
||||
intent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName);
|
||||
startActivity(intent);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment) {
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, fragment)
|
||||
.commit();
|
||||
private void updateGreeting() {
|
||||
if (displayName != null && !displayName.isEmpty()) {
|
||||
txtGreeting.setText("Olá, " + displayName.split(" ")[0] + "!");
|
||||
} else {
|
||||
txtGreeting.setText("Olá, Visitante!");
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchProfilePicture() {
|
||||
if (email == null)
|
||||
return;
|
||||
String documentId = email.replace(".", "_").replace("@", "_at_");
|
||||
FirebaseDatabase.getInstance().getReference().child("Clientes").child(documentId).child("photoUrl")
|
||||
.get().addOnSuccessListener(snapshot -> {
|
||||
if (!isDestroyed() && snapshot.exists() && snapshot.getValue(String.class) != null) {
|
||||
com.bumptech.glide.Glide.with(this).load(snapshot.getValue(String.class)).circleCrop()
|
||||
.into(imgProfile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void fetchRestaurants() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutFeatured.setVisibility(View.GONE);
|
||||
categoriesContainer.removeAllViews();
|
||||
|
||||
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("Restaurantes");
|
||||
usersRef.addValueEventListener(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
allRestaurants.clear();
|
||||
|
||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||
String role = ds.child("role").getValue(String.class);
|
||||
String accountType = ds.child("accountType").getValue(String.class);
|
||||
|
||||
// Aceitar todos os registos na coleção Restaurantes
|
||||
if (true) {
|
||||
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);
|
||||
Double ratingAvg = ds.child("ratingAvg").getValue(Double.class);
|
||||
Integer ratingCount = ds.child("ratingCount").getValue(Integer.class);
|
||||
|
||||
if (name != null && email != null) {
|
||||
Restaurant r = new Restaurant(name, cat != null ? cat : "Vário", email, false, logoUrl);
|
||||
if (ratingAvg != null)
|
||||
r.setRatingAvg(ratingAvg);
|
||||
if (ratingCount != null)
|
||||
r.setRatingCount(ratingCount);
|
||||
allRestaurants.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@androidx.annotation.NonNull DatabaseError error) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupSearch() {
|
||||
etSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
currentSearchFilter = s.toString().toLowerCase().trim();
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void applyFilters() {
|
||||
filteredRestaurants.clear();
|
||||
String normalizedSearch = normalizeString(currentSearchFilter);
|
||||
|
||||
for (Restaurant r : allRestaurants) {
|
||||
String normalizedName = normalizeString(r.getName());
|
||||
boolean matchesSearch = currentSearchFilter.isEmpty() || normalizedName.contains(normalizedSearch);
|
||||
|
||||
if (matchesSearch) {
|
||||
filteredRestaurants.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Update featured carousel with top restaurants
|
||||
List<Restaurant> featuredList = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(3, filteredRestaurants.size()); i++) {
|
||||
featuredList.add(filteredRestaurants.get(i));
|
||||
}
|
||||
|
||||
featuredAdapter = new FeaturedRestaurantAdapter(featuredList, clickListener);
|
||||
rvFeatured.setAdapter(featuredAdapter);
|
||||
layoutFeatured.setVisibility(featuredList.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
|
||||
// Update category rows
|
||||
categoriesContainer.removeAllViews();
|
||||
for (String category : CATEGORIES) {
|
||||
List<Restaurant> catList = new ArrayList<>();
|
||||
for (Restaurant r : filteredRestaurants) {
|
||||
if (category.equals(r.getCategory())) {
|
||||
catList.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (!catList.isEmpty()) {
|
||||
View rowView = android.view.LayoutInflater.from(this).inflate(R.layout.item_category_row, categoriesContainer, false);
|
||||
TextView txtTitle = rowView.findViewById(R.id.txtCategoryTitle);
|
||||
RecyclerView rvCategory = rowView.findViewById(R.id.rvCategoryRestaurants);
|
||||
|
||||
txtTitle.setText(category);
|
||||
rvCategory.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||
rvCategory.setAdapter(new FeaturedRestaurantAdapter(catList, clickListener));
|
||||
|
||||
categoriesContainer.addView(rowView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeString(String str) {
|
||||
if (str == null)
|
||||
return "";
|
||||
// Remove accents and special marks, and convert to lowercase
|
||||
String normalized = java.text.Normalizer.normalize(str, java.text.Normalizer.Form.NFD);
|
||||
normalized = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
|
||||
return normalized.toLowerCase().trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.google.firebase.firestore.CollectionReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DataSeeder {
|
||||
|
||||
public static void seedRestaurants() {
|
||||
com.google.firebase.firestore.CollectionReference restaurants = FirestoreManager.getInstance().getRestaurantsCollection();
|
||||
|
||||
List<Restaurant> sampleRestaurants = new ArrayList<>();
|
||||
|
||||
sampleRestaurants.add(createRestaurant("The Italian Job", "Italian",
|
||||
"Authentic wood-fired pizzas and homemade pasta in a cozy setting.",
|
||||
"https://images.unsplash.com/photo-1555396273-367ea4eb4db5", 4.8, "Rua de Itália, 123, Lisboa"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Sushi Zen", "Japanese",
|
||||
"Premium sushi experience with fresh ingredients flown in daily.",
|
||||
"https://images.unsplash.com/photo-1579871494447-9811cf80d66c", 4.9, "Avenida do Japão, 45, Porto"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Burger Haven", "Burgers",
|
||||
"Gourmet burgers made with 100% aged beef and unique toppings.",
|
||||
"https://images.unsplash.com/photo-1571091718767-18b5b1457add", 4.5, "Praça do Hambúrguer, 10, Braga"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Taco Libre", "Mexican",
|
||||
"Vibrant street-style Mexican food and craft margaritas.",
|
||||
"https://images.unsplash.com/photo-1565299585323-38d6b0865b47", 4.6, "Rua do México, 88, Faro"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Steakhouse Prime", "Steakhouse",
|
||||
"Expertly grilled premium cuts and an extensive wine list.",
|
||||
"https://images.unsplash.com/photo-1546241072-48010ad28c2c", 4.7, "Travessa do Bife, 5, Coimbra"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Le Petit Bistro", "French",
|
||||
"A slice of Paris serving classic French dishes and fine wine.",
|
||||
"https://images.unsplash.com/photo-1550966842-28a2a2d3efd1", 4.4, "Rua de França, 22, Évora"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Green Garden", "Vegetarian",
|
||||
"Sustainable and delicious plant-based cuisine for everyone.",
|
||||
"https://images.unsplash.com/photo-1512621776951-a57141f2eefd", 4.6, "Alameda Verde, 15, Aveiro"));
|
||||
|
||||
sampleRestaurants.add(createRestaurant("Ocean's Catch", "Seafood",
|
||||
"The freshest seafood and shellfish with a beautiful ocean view.",
|
||||
"https://images.unsplash.com/photo-1551248429-42405d2d9cd8", 4.8, "Estrada da Praia, 1, Cascais"));
|
||||
|
||||
for (Restaurant r : sampleRestaurants) {
|
||||
restaurants.document().set(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static Restaurant createRestaurant(String name, String cuisine, String desc, String url, double rating, String address) {
|
||||
Restaurant r = new Restaurant();
|
||||
r.setName(name);
|
||||
r.setCuisine(cuisine);
|
||||
r.setDescription(desc);
|
||||
r.setImageURL(url);
|
||||
r.setRating(rating);
|
||||
r.setAddress(address);
|
||||
|
||||
Map<String, String> hours = new HashMap<>();
|
||||
hours.put("open", "12:00");
|
||||
hours.put("close", "23:00");
|
||||
r.setOpeningHours(hours);
|
||||
|
||||
List<String> slots = new ArrayList<>();
|
||||
slots.add("12:00"); slots.add("13:00"); slots.add("14:00");
|
||||
slots.add("19:00"); slots.add("20:00"); slots.add("21:00"); slots.add("22:00");
|
||||
r.setAvailableSlots(slots);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
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;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.app.TimePickerDialog;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.example.pap_teste.models.ScheduleDay;
|
||||
|
||||
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"};
|
||||
private String[] dayNames = {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"};
|
||||
private String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||
private List<View> scheduleViews = new ArrayList<>();
|
||||
|
||||
@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("Restaurantes");
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
LinearLayout containerSchedule = findViewById(R.id.containerSchedule);
|
||||
LayoutInflater inflater = LayoutInflater.from(this);
|
||||
for (int i = 0; i < dayNames.length; i++) {
|
||||
View view = inflater.inflate(R.layout.item_schedule_day, containerSchedule, false);
|
||||
TextView tvDayName = view.findViewById(R.id.tvDayName);
|
||||
TextView tvOpenTime = view.findViewById(R.id.tvOpenTime);
|
||||
TextView tvCloseTime = view.findViewById(R.id.tvCloseTime);
|
||||
CheckBox cbClosed = view.findViewById(R.id.cbClosed);
|
||||
|
||||
tvDayName.setText(dayNames[i]);
|
||||
|
||||
tvOpenTime.setOnClickListener(v -> showTimePicker(tvOpenTime));
|
||||
tvCloseTime.setOnClickListener(v -> showTimePicker(tvCloseTime));
|
||||
|
||||
cbClosed.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
tvOpenTime.setEnabled(!isChecked);
|
||||
tvCloseTime.setEnabled(!isChecked);
|
||||
});
|
||||
|
||||
containerSchedule.addView(view);
|
||||
scheduleViews.add(view);
|
||||
}
|
||||
|
||||
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 showTimePicker(TextView targetTextView) {
|
||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||
new TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||
targetTextView.setText(String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute));
|
||||
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).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);
|
||||
}
|
||||
}
|
||||
if (snapshot.hasChild("schedule")) {
|
||||
for (int i = 0; i < dayKeys.length; i++) {
|
||||
DataSnapshot daySnapshot = snapshot.child("schedule").child(dayKeys[i]);
|
||||
if (daySnapshot.exists()) {
|
||||
ScheduleDay sd = daySnapshot.getValue(ScheduleDay.class);
|
||||
if (sd != null) {
|
||||
View view = scheduleViews.get(i);
|
||||
CheckBox cbClosed = view.findViewById(R.id.cbClosed);
|
||||
TextView tvOpenTime = view.findViewById(R.id.tvOpenTime);
|
||||
TextView tvCloseTime = view.findViewById(R.id.tvCloseTime);
|
||||
|
||||
cbClosed.setChecked(sd.isClosed());
|
||||
if (sd.getOpenTime() != null) tvOpenTime.setText(sd.getOpenTime());
|
||||
if (sd.getCloseTime() != null) tvCloseTime.setText(sd.getCloseTime());
|
||||
|
||||
tvOpenTime.setEnabled(!sd.isClosed());
|
||||
tvCloseTime.setEnabled(!sd.isClosed());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
Map<String, ScheduleDay> scheduleMap = new HashMap<>();
|
||||
for (int i = 0; i < dayKeys.length; i++) {
|
||||
View view = scheduleViews.get(i);
|
||||
CheckBox cbClosed = view.findViewById(R.id.cbClosed);
|
||||
TextView tvOpenTime = view.findViewById(R.id.tvOpenTime);
|
||||
TextView tvCloseTime = view.findViewById(R.id.tvCloseTime);
|
||||
|
||||
scheduleMap.put(dayKeys[i], new ScheduleDay(cbClosed.isChecked(), tvOpenTime.getText().toString(), tvCloseTime.getText().toString()));
|
||||
}
|
||||
updates.put("schedule", scheduleMap);
|
||||
|
||||
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,365 @@
|
||||
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) {
|
||||
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();
|
||||
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.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
reservas.clear();
|
||||
String targetEmail = restaurantEmail != null ? restaurantEmail.trim() : "";
|
||||
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())) {
|
||||
String rEmail = r.getRestauranteEmail();
|
||||
if (rEmail != null && rEmail.trim().equalsIgnoreCase(targetEmail)) {
|
||||
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())) {
|
||||
mostrarMesasDisponiveis();
|
||||
} else if ("Confirmada".equals(item.getEstado()) || item.getEstado().startsWith("Confirmada (Mesa")) {
|
||||
showConcluirDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnRecusar != null) {
|
||||
btnRecusar.setOnClickListener(v -> showRecusarDialog());
|
||||
}
|
||||
|
||||
if (btnApagar != null) {
|
||||
btnApagar.setOnClickListener(v -> apagarReserva());
|
||||
}
|
||||
}
|
||||
|
||||
private void mostrarMesasDisponiveis() {
|
||||
if (selectedIndex < 0 || selectedIndex >= reservas.size()) {
|
||||
Toast.makeText(this, "Selecione uma reserva.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
||||
|
||||
com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas");
|
||||
mesasRef.orderByChild("restauranteEmail").equalTo(restaurantEmail).addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
List<com.example.pap_teste.models.Mesa> mesasLivres = new ArrayList<>();
|
||||
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 && m.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre") && m.getCapacidade() >= item.getPessoas()) {
|
||||
m.setId(ds.getKey());
|
||||
mesasLivres.add(m);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesasLivres.isEmpty()) {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(DetalhesReservasActivity.this)
|
||||
.setTitle("Sem mesas disponíveis")
|
||||
.setMessage("Não há mesas livres com capacidade suficiente para " + item.getPessoas() + " pessoas. Deseja confirmar a reserva mesmo assim (sem mesa atribuída)?")
|
||||
.setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada", null))
|
||||
.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(DetalhesReservasActivity.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 com.google.firebase.database.DatabaseError error) {
|
||||
Toast.makeText(DetalhesReservasActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void confirmarReservaComMesa(com.example.pap_teste.models.Reserva reserva, com.example.pap_teste.models.Mesa mesa) {
|
||||
String novoEstado = "Confirmada (Mesa " + mesa.getNumero() + ")";
|
||||
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
updates.put("estado", novoEstado);
|
||||
updates.put("motivo", null); // limpar possível motivo antigo
|
||||
|
||||
databaseReference.child(reserva.getId()).updateChildren(updates).addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
com.google.firebase.database.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();
|
||||
reserva.setEstado(novoEstado);
|
||||
reserva.setMotivo(null);
|
||||
mostrarDetalhe(reserva);
|
||||
});
|
||||
} else {
|
||||
Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showConcluirDialog() {
|
||||
if (selectedIndex < 0) return;
|
||||
android.widget.EditText input = new android.widget.EditText(this);
|
||||
input.setHint("Notas de conclusão (opcional)");
|
||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("Concluir Reserva")
|
||||
.setMessage("Deseja adicionar alguma nota ao concluir esta reserva?")
|
||||
.setView(input)
|
||||
.setPositiveButton("Concluir", (dialog, which) -> {
|
||||
String motivo = input.getText().toString();
|
||||
atualizarEstadoSelecionado("Concluída", motivo.isEmpty() ? null : motivo);
|
||||
})
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.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 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, String motivo) {
|
||||
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);
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
updates.put("estado", novoEstado);
|
||||
if (motivo != null && !motivo.isEmpty()) {
|
||||
updates.put("motivo", motivo);
|
||||
} else {
|
||||
updates.put("motivo", null);
|
||||
}
|
||||
|
||||
databaseReference.child(item.getId()).updateChildren(updates).addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
txtMensagem
|
||||
.setText(String.format("Reserva de %s marcada como %s.", item.getClienteEmail(), novoEstado));
|
||||
item.setEstado(novoEstado);
|
||||
item.setMotivo(motivo);
|
||||
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());
|
||||
|
||||
String estadoTexto = item.getEstado();
|
||||
if (item.getMotivo() != null && !item.getMotivo().isEmpty()) {
|
||||
estadoTexto += " (" + item.getMotivo() + ")";
|
||||
}
|
||||
txtEstado.setText(String.format("Estado: %s", estadoTexto));
|
||||
|
||||
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 "Concluída":
|
||||
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||
btnRecusar.setVisibility(android.view.View.GONE);
|
||||
btnApagar.setVisibility(android.view.View.VISIBLE);
|
||||
break;
|
||||
default:
|
||||
if (item.getEstado() != null && item.getEstado().startsWith("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);
|
||||
} else {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,324 @@
|
||||
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);
|
||||
// Temporariamente usando o layout do cliente ou um placeholder se necessário
|
||||
setContentView(R.layout.activity_client_dashboard);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
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; // Can't proceed without email
|
||||
}
|
||||
}
|
||||
final String finalEmail = email;
|
||||
|
||||
btnOpenWalkIns.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, ListaEsperaActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
btnStaff.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, GestaoStaffActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
btnGerirMesas.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, GerirMesasActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
btnDetails.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, DetalhesReservasActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
btnSettings.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, DefinicoesAdminActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
findViewById(R.id.cardReservasHoje).setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, DetalhesReservasActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
if (btnBack != null) {
|
||||
|
||||
btnBack.setOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
loadProximasReservas();
|
||||
loadEstatisticas();
|
||||
checkPendingReservationsOnLogin();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
final String targetEmail = email != null ? email.trim() : "";
|
||||
|
||||
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")
|
||||
.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())) {
|
||||
String rEmail = r.getRestauranteEmail();
|
||||
if (rEmail != null && rEmail.trim().equalsIgnoreCase(targetEmail)) {
|
||||
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 != null ? email.trim() : "";
|
||||
|
||||
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.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.getRestauranteEmail() != null && r.getRestauranteEmail().trim().equalsIgnoreCase(finalEmail)) {
|
||||
if (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 totalMesas = 0;
|
||||
int mesasOcupadas = 0;
|
||||
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 && (mesa.getRestauranteEmail() == null || mesa.getRestauranteEmail().trim().equalsIgnoreCase(finalEmail))) {
|
||||
totalMesas++;
|
||||
if ("Ocupada".equalsIgnoreCase(mesa.getEstado()) || "Reservada".equalsIgnoreCase(mesa.getEstado())) {
|
||||
mesasOcupadas++;
|
||||
} else if ("Livre".equalsIgnoreCase(mesa.getEstado())) {
|
||||
mesasLivres++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txtMesasLivres != null) txtMesasLivres.setText(mesasOcupadas + " / " + totalMesas);
|
||||
TextView txtMesasLivresSub = findViewById(R.id.txtMesasLivresSub);
|
||||
if (txtMesasLivresSub != null) txtMesasLivresSub.setText(mesasLivres + " livres");
|
||||
}
|
||||
@Override
|
||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkPendingReservationsOnLogin() {
|
||||
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 != null ? email.trim() : "";
|
||||
|
||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas")
|
||||
.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
java.util.List<com.example.pap_teste.models.Reserva> pendingReservations = new java.util.ArrayList<>();
|
||||
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.getRestauranteEmail() != null && r.getRestauranteEmail().trim().equalsIgnoreCase(finalEmail)) {
|
||||
if ("Pendente".equals(r.getEstado())) {
|
||||
pendingReservations.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pendingReservations.isEmpty()) {
|
||||
showPendingReservationsDialog(pendingReservations);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showPendingReservationsDialog(java.util.List<com.example.pap_teste.models.Reserva> pendingReservations) {
|
||||
if (isFinishing() || isDestroyed()) return;
|
||||
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(this);
|
||||
builder.setTitle("Reservas Pendentes (" + pendingReservations.size() + ")");
|
||||
|
||||
StringBuilder message = new StringBuilder("Tem reservas a aguardar confirmação:\n\n");
|
||||
for (com.example.pap_teste.models.Reserva r : pendingReservations) {
|
||||
message.append("• ").append(r.getData()).append(" às ").append(r.getHora())
|
||||
.append(" - ").append(r.getPessoas()).append(" pessoas")
|
||||
.append("\n Cliente: ").append(r.getClienteEmail()).append("\n\n");
|
||||
}
|
||||
|
||||
builder.setMessage(message.toString());
|
||||
builder.setPositiveButton("Ver Reservas", (dialog, which) -> {
|
||||
Intent intent = new Intent(EstablishmentDashboardActivity.this, DetalhesReservasActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL));
|
||||
startActivity(intent);
|
||||
});
|
||||
builder.setNegativeButton("Fechar", (dialog, which) -> dialog.dismiss());
|
||||
|
||||
android.app.AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
android.widget.TextView textView = dialog.findViewById(android.R.id.message);
|
||||
if (textView != null) {
|
||||
textView.setTextSize(18);
|
||||
textView.setGravity(android.view.Gravity.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,14 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.example.pap_teste.models.Review;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.Query;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import android.widget.Button;
|
||||
|
||||
public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
|
||||
@@ -46,20 +17,18 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private State currentState = State.LIST;
|
||||
private Restaurant selectedRestaurant = null;
|
||||
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
||||
|
||||
private View scrollReservaDetails;
|
||||
private RecyclerView rvRestaurants;
|
||||
private TextView txtTitle;
|
||||
private ProgressBar progressBar;
|
||||
private android.view.View scrollReservaDetails;
|
||||
private androidx.recyclerview.widget.RecyclerView rvRestaurants;
|
||||
private android.widget.TextView txtTitle;
|
||||
private android.widget.ProgressBar progressBar;
|
||||
|
||||
private String selectedDate = null;
|
||||
private String selectedTime = null;
|
||||
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
|
||||
private final ActivityResultLauncher<Intent> photoPickerLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
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();
|
||||
@@ -74,15 +43,11 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_explorar_restaurantes);
|
||||
|
||||
View root = findViewById(R.id.explorarRoot);
|
||||
if (root != null) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
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);
|
||||
@@ -94,6 +59,11 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
back.setOnClickListener(v -> handleBackNavigation());
|
||||
}
|
||||
|
||||
if (getIntent().hasExtra("restaurant")) {
|
||||
selectedRestaurant = (com.example.pap_teste.models.Restaurant) getIntent().getSerializableExtra("restaurant");
|
||||
currentState = State.DETAILS;
|
||||
}
|
||||
|
||||
setupRestaurantList();
|
||||
updateViewState();
|
||||
}
|
||||
@@ -108,8 +78,9 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void updateViewState() {
|
||||
rvRestaurants.setVisibility(currentState == State.LIST ? View.VISIBLE : View.GONE);
|
||||
scrollReservaDetails.setVisibility(currentState == State.DETAILS ? View.VISIBLE : View.GONE);
|
||||
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");
|
||||
@@ -118,118 +89,262 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
||||
setupReservationOptions();
|
||||
loadRestaurantRating();
|
||||
|
||||
boolean isFechado = selectedRestaurant != null && selectedRestaurant.isFechadoWebsite();
|
||||
findViewById(R.id.btnSelectDate).setEnabled(!isFechado);
|
||||
findViewById(R.id.btnSelectTime).setEnabled(!isFechado);
|
||||
android.view.View etPartySize = findViewById(R.id.etPartySize);
|
||||
if (etPartySize != null) etPartySize.setEnabled(!isFechado);
|
||||
findViewById(R.id.btnConfirmarReserva).setEnabled(!isFechado);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRestaurantRating() {
|
||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
||||
return;
|
||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||
|
||||
TextView txtAvgRating = findViewById(R.id.txtAvgRating);
|
||||
TextView txtTotalReviews = findViewById(R.id.txtTotalReviews);
|
||||
android.widget.TextView txtAvgRating = findViewById(R.id.txtAvgRating);
|
||||
android.widget.TextView txtTotalReviews = findViewById(R.id.txtTotalReviews);
|
||||
|
||||
db.collection("Restaurantes").whereEqualTo("email", selectedRestaurant.getEmail()).get()
|
||||
.addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
if (!queryDocumentSnapshots.isEmpty()) {
|
||||
QueryDocumentSnapshot doc = (QueryDocumentSnapshot) queryDocumentSnapshots.getDocuments().get(0);
|
||||
Double avg = doc.getDouble("ratingAvg");
|
||||
Long count = doc.getLong("ratingCount");
|
||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Restaurantes")
|
||||
.child(encodedEmail)
|
||||
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
Double avg = snapshot.child("ratingAvg").getValue(Double.class);
|
||||
Integer count = snapshot.child("ratingCount").getValue(Integer.class);
|
||||
|
||||
if (avg != null && count != null && count > 0) {
|
||||
txtAvgRating.setText(String.format(Locale.getDefault(), "%.1f", avg));
|
||||
txtTotalReviews.setText(String.format(Locale.getDefault(), "(%d avaliações)", count));
|
||||
txtAvgRating.setText(String.format(java.util.Locale.getDefault(), "%.1f", avg));
|
||||
txtTotalReviews
|
||||
.setText(String.format(java.util.Locale.getDefault(), "(%d avaliações)", count));
|
||||
} else {
|
||||
txtAvgRating.setText("0.0");
|
||||
txtTotalReviews.setText("(0 avaliações)");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRestaurantList() {
|
||||
String filter = getIntent().getStringExtra("category_filter");
|
||||
List<Restaurant> restaurantsList = new ArrayList<>();
|
||||
|
||||
Query query = db.collection("Restaurantes");
|
||||
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("Restaurantes");
|
||||
|
||||
com.google.firebase.database.Query query = usersRef;
|
||||
if (filter != null) {
|
||||
query = query.whereEqualTo("category", filter);
|
||||
query = usersRef.orderByChild("category").equalTo(filter);
|
||||
}
|
||||
|
||||
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
||||
|
||||
query.get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
restaurantsList.clear();
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
Restaurant r = doc.toObject(Restaurant.class);
|
||||
r.setId(doc.getId());
|
||||
restaurantsList.add(r);
|
||||
query.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
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);
|
||||
|
||||
// Aceitar todos os registos na coleção Restaurantes
|
||||
if (true) {
|
||||
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) {
|
||||
com.example.pap_teste.models.Restaurant rest = new com.example.pap_teste.models.Restaurant(name, cat, email, false, logoUrl);
|
||||
Boolean fechadoWebsite = ds.child("fechadoWebsite").getValue(Boolean.class);
|
||||
if (fechadoWebsite != null) rest.setFechadoWebsite(fechadoWebsite);
|
||||
|
||||
if (ds.hasChild("schedule")) {
|
||||
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||
for (com.google.firebase.database.DataSnapshot schDs : ds.child("schedule").getChildren()) {
|
||||
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||
schMap.put(schDs.getKey(), sd);
|
||||
}
|
||||
rest.setSchedule(schMap);
|
||||
} else if (ds.hasChild("operatingHours")) {
|
||||
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||
for (com.google.firebase.database.DataSnapshot schDs : ds.child("operatingHours").getChildren()) {
|
||||
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||
if (schDs.hasChild("isOpen")) {
|
||||
Boolean isOpen = schDs.child("isOpen").getValue(Boolean.class);
|
||||
if (isOpen != null && sd != null) {
|
||||
sd.setClosed(!isOpen);
|
||||
}
|
||||
}
|
||||
schMap.put(schDs.getKey(), sd);
|
||||
}
|
||||
rest.setSchedule(schMap);
|
||||
}
|
||||
restaurantsList.add(rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RestaurantAdapter adapter = new RestaurantAdapter(restaurantsList, restaurant -> {
|
||||
selectedRestaurant = restaurant;
|
||||
currentState = State.DETAILS;
|
||||
updateViewState();
|
||||
});
|
||||
rvRestaurants.setAdapter(adapter);
|
||||
}
|
||||
|
||||
RestaurantAdapter adapter = new RestaurantAdapter(restaurantsList, restaurant -> {
|
||||
selectedRestaurant = restaurant;
|
||||
currentState = State.DETAILS;
|
||||
updateViewState();
|
||||
});
|
||||
rvRestaurants.setAdapter(adapter);
|
||||
}).addOnFailureListener(e -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Erro ao carregar restaurantes.", Toast.LENGTH_SHORT).show();
|
||||
@Override
|
||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
android.widget.Toast.makeText(ExplorarRestaurantesActivity.this, "Erro ao carregar restaurantes.",
|
||||
android.widget.Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupReservationOptions() {
|
||||
Button btnDate = findViewById(R.id.btnSelectDate);
|
||||
Button btnTime = findViewById(R.id.btnSelectTime);
|
||||
Button btnVerAvaliacoes = findViewById(R.id.btnVerAvaliacoes);
|
||||
Button btnUploadFoto = findViewById(R.id.btnUploadFoto);
|
||||
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 -> {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||
btnDate.setText(selectedDate);
|
||||
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||
android.app.DatePickerDialog dpd = new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||
java.util.Calendar selectedCal = java.util.Calendar.getInstance();
|
||||
selectedCal.set(year, month, dayOfMonth);
|
||||
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||
|
||||
boolean isClosed = selectedRestaurant.isClosedForDay(dayOfWeek);
|
||||
|
||||
if (isClosed) {
|
||||
android.widget.Toast.makeText(this, "O restaurante encontra-se encerrado na data selecionada.", android.widget.Toast.LENGTH_LONG).show();
|
||||
selectedDate = null;
|
||||
btnDate.setText("Selecionar Data");
|
||||
btnTime.setEnabled(false);
|
||||
} else {
|
||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||
btnDate.setText(selectedDate);
|
||||
btnTime.setEnabled(true);
|
||||
}
|
||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH), cal.get(java.util.Calendar.DAY_OF_MONTH));
|
||||
dpd.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
|
||||
dpd.show();
|
||||
});
|
||||
|
||||
btnTime.setOnClickListener(v -> {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
if (selectedDate == null) {
|
||||
android.widget.Toast.makeText(this, "Por favor, selecione primeiro a data.", android.widget.Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||
selectedTime = String.format(Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||
btnTime.setText(selectedTime);
|
||||
}, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
|
||||
String chosenTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||
|
||||
boolean isOutOfHours = false;
|
||||
if (selectedDate != null && selectedRestaurant.getSchedule() != null) {
|
||||
try {
|
||||
java.util.Date d = new java.text.SimpleDateFormat("d/M/yyyy", java.util.Locale.getDefault()).parse(selectedDate);
|
||||
java.util.Calendar selectedCal = java.util.Calendar.getInstance();
|
||||
if (d != null) selectedCal.setTime(d);
|
||||
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||
|
||||
com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getScheduleForDay(dayOfWeek);
|
||||
if (sd != null && !sd.isClosed()) {
|
||||
if (sd.getOpenTime() != null && sd.getCloseTime() != null && !sd.getOpenTime().isEmpty() && !sd.getCloseTime().isEmpty()) {
|
||||
String open = sd.getOpenTime();
|
||||
String close = sd.getCloseTime();
|
||||
if (open.compareTo(close) < 0) {
|
||||
if (chosenTime.compareTo(open) < 0 || chosenTime.compareTo(close) > 0) {
|
||||
isOutOfHours = true;
|
||||
}
|
||||
} else {
|
||||
if (chosenTime.compareTo(open) < 0 && chosenTime.compareTo(close) > 0) {
|
||||
isOutOfHours = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
if (isOutOfHours) {
|
||||
android.widget.Toast.makeText(this, "Por favor selecione uma hora dentro do horário de funcionamento.", android.widget.Toast.LENGTH_LONG).show();
|
||||
selectedTime = null;
|
||||
btnTime.setText("Selecionar Hora");
|
||||
} else {
|
||||
selectedTime = chosenTime;
|
||||
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 -> {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK);
|
||||
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;
|
||||
|
||||
// Simulating upload for now as Storage config might vary, but keeping logic
|
||||
Toast.makeText(this, "Upload de foto em breve...", Toast.LENGTH_SHORT).show();
|
||||
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;
|
||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
||||
return;
|
||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_reviews_list, null);
|
||||
RecyclerView rvReviews = dialogView.findViewById(R.id.rvReviewsList);
|
||||
TextView txtEmpty = dialogView.findViewById(R.id.txtEmptyReviews);
|
||||
Button btnClose = dialogView.findViewById(R.id.btnCloseReviews);
|
||||
Button btnAdd = dialogView.findViewById(R.id.btnAddReview);
|
||||
android.view.View dialogView = getLayoutInflater().inflate(R.layout.dialog_reviews_list, null);
|
||||
androidx.recyclerview.widget.RecyclerView rvReviews = dialogView.findViewById(R.id.rvReviewsList);
|
||||
android.widget.TextView txtEmpty = dialogView.findViewById(R.id.txtEmptyReviews);
|
||||
android.widget.Button btnClose = dialogView.findViewById(R.id.btnCloseReviews);
|
||||
android.widget.Button btnAdd = dialogView.findViewById(R.id.btnAddReview);
|
||||
|
||||
rvReviews.setLayoutManager(new LinearLayoutManager(this));
|
||||
rvReviews.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this));
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||
androidx.appcompat.app.AlertDialog dialog = new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setView(dialogView)
|
||||
.create();
|
||||
if (dialog.getWindow() != null) dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
btnClose.setOnClickListener(v -> dialog.dismiss());
|
||||
btnAdd.setOnClickListener(v -> {
|
||||
@@ -237,152 +352,279 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||
addReviewDialog();
|
||||
});
|
||||
|
||||
String restId = selectedRestaurant.getId();
|
||||
db.collection("Reviews").whereEqualTo("restaurantId", restId).addSnapshotListener((value, error) -> {
|
||||
if (value == null) return;
|
||||
List<Review> reviewsList = new ArrayList<>();
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
Review rev = doc.toObject(Review.class);
|
||||
rev.setKey(doc.getId());
|
||||
reviewsList.add(rev);
|
||||
com.google.firebase.database.DatabaseReference reviewsRef = com.google.firebase.database.FirebaseDatabase
|
||||
.getInstance()
|
||||
.getReference("reviews").child(encodedEmail);
|
||||
|
||||
String currentUserEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
||||
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||
: null;
|
||||
|
||||
reviewsRef.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
java.util.List<com.example.pap_teste.models.Review> 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);
|
||||
String uEmail = dst.child("userEmail").getValue(String.class);
|
||||
Float rating = dst.child("rating").getValue(Float.class);
|
||||
|
||||
if (rating == null)
|
||||
rating = 0f;
|
||||
|
||||
com.example.pap_teste.models.Review rev = new com.example.pap_teste.models.Review(
|
||||
dst.getKey(), author, text, rating, uEmail);
|
||||
reviewsList.add(rev);
|
||||
}
|
||||
|
||||
if (reviewsList.isEmpty()) {
|
||||
txtEmpty.setVisibility(android.view.View.VISIBLE);
|
||||
rvReviews.setVisibility(android.view.View.GONE);
|
||||
} else {
|
||||
txtEmpty.setVisibility(android.view.View.GONE);
|
||||
rvReviews.setVisibility(android.view.View.VISIBLE);
|
||||
}
|
||||
|
||||
ReviewAdapter adapter = new ReviewAdapter(reviewsList, currentUserEmail, review -> {
|
||||
// Confirmação de Apagar
|
||||
new androidx.appcompat.app.AlertDialog.Builder(ExplorarRestaurantesActivity.this)
|
||||
.setTitle("Apagar Avaliação")
|
||||
.setMessage("Tens a certeza que queres apagar esta avaliação?")
|
||||
.setPositiveButton("Sim", (d, w) -> {
|
||||
deleteReview(review.getKey());
|
||||
d.dismiss();
|
||||
})
|
||||
.setNegativeButton("Não", null)
|
||||
.show();
|
||||
});
|
||||
rvReviews.setAdapter(adapter);
|
||||
}
|
||||
|
||||
if (reviewsList.isEmpty()) {
|
||||
txtEmpty.setVisibility(View.VISIBLE);
|
||||
rvReviews.setVisibility(View.GONE);
|
||||
} else {
|
||||
txtEmpty.setVisibility(View.GONE);
|
||||
rvReviews.setVisibility(View.VISIBLE);
|
||||
@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();
|
||||
}
|
||||
|
||||
String currentUserEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||
? FirebaseAuth.getInstance().getCurrentUser().getEmail() : null;
|
||||
|
||||
ReviewAdapter adapter = new ReviewAdapter(reviewsList, currentUserEmail, review -> {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Apagar Avaliação")
|
||||
.setMessage("Tens a certeza que queres apagar esta avaliação?")
|
||||
.setPositiveButton("Sim", (d, w) -> deleteReview(review.getKey()))
|
||||
.setNegativeButton("Não", null)
|
||||
.show();
|
||||
});
|
||||
rvReviews.setAdapter(adapter);
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void deleteReview(String reviewKey) {
|
||||
db.collection("Reviews").document(reviewKey).delete().addOnSuccessListener(aVoid -> {
|
||||
Snackbar.make(findViewById(R.id.explorarRoot), "Avaliação removida", Snackbar.LENGTH_SHORT).show();
|
||||
recalculateRestaurantAverage(selectedRestaurant.getId());
|
||||
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.child(reviewKey).removeValue().addOnSuccessListener(aVoid -> {
|
||||
com.google.android.material.snackbar.Snackbar.make(findViewById(R.id.explorarRoot), "Avaliação removida",
|
||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||
recalculateRestaurantAverage(encodedEmail);
|
||||
});
|
||||
}
|
||||
|
||||
private void recalculateRestaurantAverage(String restaurantId) {
|
||||
db.collection("Reviews").whereEqualTo("restaurantId", restaurantId).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
double totalRating = 0;
|
||||
int count = 0;
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
Double r = doc.getDouble("rating");
|
||||
if (r != null) {
|
||||
totalRating += r;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
double newAvg = count > 0 ? (totalRating / count) : 0.0;
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("ratingAvg", newAvg);
|
||||
updates.put("ratingCount", count);
|
||||
db.collection("Restaurantes").document(restaurantId).update(updates);
|
||||
});
|
||||
private void recalculateRestaurantAverage(String encodedEmail) {
|
||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews").child(encodedEmail)
|
||||
.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
double totalRating = 0;
|
||||
int count = 0;
|
||||
for (com.google.firebase.database.DataSnapshot dst : snapshot.getChildren()) {
|
||||
Float r = dst.child("rating").getValue(Float.class);
|
||||
if (r != null) {
|
||||
totalRating += r;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
double newAvg = count > 0 ? (totalRating / count) : 0.0;
|
||||
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
updates.put("ratingAvg", newAvg);
|
||||
updates.put("ratingCount", count);
|
||||
|
||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Restaurantes")
|
||||
.child(encodedEmail).updateChildren(updates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addReviewDialog() {
|
||||
if (selectedRestaurant == null) return;
|
||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
||||
return;
|
||||
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_leave_review, null);
|
||||
com.example.pap_teste.components.InteractiveRatingBar ratingBar = dialogView.findViewById(R.id.interactiveRatingBar);
|
||||
EditText input = dialogView.findViewById(R.id.etReviewComment);
|
||||
Button btnSubmit = dialogView.findViewById(R.id.btnSubmitReview);
|
||||
Button btnCancel = dialogView.findViewById(R.id.btnCancelReview);
|
||||
android.view.View dialogView = getLayoutInflater().inflate(R.layout.dialog_leave_review, null);
|
||||
com.example.pap_teste.components.InteractiveRatingBar ratingBar = dialogView
|
||||
.findViewById(R.id.interactiveRatingBar);
|
||||
android.widget.EditText input = dialogView.findViewById(R.id.etReviewComment);
|
||||
android.widget.Button btnSubmit = dialogView.findViewById(R.id.btnSubmitReview);
|
||||
android.widget.Button btnCancel = dialogView.findViewById(R.id.btnCancelReview);
|
||||
|
||||
btnSubmit.setEnabled(false);
|
||||
btnSubmit.setAlpha(0.5f);
|
||||
|
||||
input.addTextChangedListener(new android.text.TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
boolean hasText = s != null && s.toString().trim().length() > 0;
|
||||
btnSubmit.setEnabled(hasText);
|
||||
btnSubmit.setAlpha(hasText ? 1.0f : 0.5f);
|
||||
}
|
||||
@Override public void afterTextChanged(android.text.Editable s) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(android.text.Editable s) {
|
||||
}
|
||||
});
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(this).setView(dialogView).create();
|
||||
if (dialog.getWindow() != null) dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
androidx.appcompat.app.AlertDialog dialog = new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setView(dialogView)
|
||||
.create();
|
||||
|
||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
btnCancel.setOnClickListener(v -> dialog.dismiss());
|
||||
btnSubmit.setOnClickListener(v -> {
|
||||
float rating = (float) ratingBar.getRating();
|
||||
String revText = input.getText().toString().trim();
|
||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||
|
||||
String author = "Visitante";
|
||||
String userEmail = user != null ? user.getEmail() : null;
|
||||
|
||||
submitReviewToFirestore(author, revText, rating, userEmail);
|
||||
dialog.dismiss();
|
||||
|
||||
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("Clientes").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, rating);
|
||||
dialog.dismiss();
|
||||
}).addOnFailureListener(e -> {
|
||||
submitReviewToFirebase("Visitante", revText, rating);
|
||||
dialog.dismiss();
|
||||
});
|
||||
} else {
|
||||
submitReviewToFirebase("Visitante", revText, rating);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void submitReviewToFirestore(String author, String text, float rating, String userEmail) {
|
||||
Map<String, Object> review = new HashMap<>();
|
||||
review.put("restaurantId", selectedRestaurant.getId());
|
||||
review.put("author", author);
|
||||
review.put("text", text);
|
||||
review.put("rating", rating);
|
||||
review.put("userEmail", userEmail);
|
||||
review.put("timestamp", com.google.firebase.Timestamp.now());
|
||||
private void submitReviewToFirebase(String authorName, String revText, float newRating) {
|
||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
||||
|
||||
db.collection("Reviews").add(review).addOnSuccessListener(documentReference -> {
|
||||
Snackbar.make(findViewById(R.id.explorarRoot), "Obrigado pela tua avaliação!", Snackbar.LENGTH_LONG).show();
|
||||
recalculateRestaurantAverage(selectedRestaurant.getId());
|
||||
com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance()
|
||||
.getCurrentUser();
|
||||
String uEmail = currentUser != null ? currentUser.getEmail() : null;
|
||||
|
||||
// 1. Guardar a Review
|
||||
java.util.Map<String, Object> review = new java.util.HashMap<>();
|
||||
review.put("author", authorName);
|
||||
review.put("text", revText);
|
||||
review.put("rating", newRating);
|
||||
review.put("userEmail", uEmail);
|
||||
review.put("timestamp", com.google.firebase.database.ServerValue.TIMESTAMP);
|
||||
|
||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews")
|
||||
.child(encodedEmail).push().setValue(review).addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
com.google.android.material.snackbar.Snackbar
|
||||
.make(findViewById(R.id.explorarRoot), "Obrigado pela tua avaliação!",
|
||||
com.google.android.material.snackbar.Snackbar.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Atualizar Média no Restaurante
|
||||
com.google.firebase.database.DatabaseReference restRef = com.google.firebase.database.FirebaseDatabase
|
||||
.getInstance().getReference("Restaurantes").child(encodedEmail);
|
||||
restRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
Double currentAvg = snapshot.child("ratingAvg").getValue(Double.class);
|
||||
Integer currentCount = snapshot.child("ratingCount").getValue(Integer.class);
|
||||
|
||||
if (currentAvg == null)
|
||||
currentAvg = 0.0;
|
||||
if (currentCount == null)
|
||||
currentCount = 0;
|
||||
|
||||
double newAvg = ((currentAvg * currentCount) + newRating) / (currentCount + 1);
|
||||
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
updates.put("ratingAvg", newAvg);
|
||||
updates.put("ratingCount", currentCount + 1);
|
||||
|
||||
restRef.updateChildren(updates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveReservation() {
|
||||
EditText etPartySize = findViewById(R.id.etPartySize);
|
||||
android.widget.EditText etPartySize = findViewById(R.id.etPartySize);
|
||||
int partySize = 0;
|
||||
try {
|
||||
partySize = Integer.parseInt(etPartySize.getText().toString());
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
||||
Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.", Toast.LENGTH_SHORT).show();
|
||||
android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.",
|
||||
android.widget.Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String userEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||
? FirebaseAuth.getInstance().getCurrentUser().getEmail() : "cliente@teste.com";
|
||||
String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
||||
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||
: "cliente@teste.com";
|
||||
|
||||
Reserva reserva = new Reserva(
|
||||
null,
|
||||
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"
|
||||
);
|
||||
"Pendente");
|
||||
|
||||
db.collection("Reservas").add(reserva).addOnSuccessListener(documentReference -> {
|
||||
Toast.makeText(this, "Reserva solicitada com sucesso!", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}).addOnFailureListener(e -> Toast.makeText(this, "Erro ao salvar reserva.", Toast.LENGTH_SHORT).show());
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,47 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
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 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.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
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 RecyclerView rv;
|
||||
private androidx.recyclerview.widget.RecyclerView rv;
|
||||
private RestaurantAdapter adapter;
|
||||
private List<Restaurant> list;
|
||||
private ProgressBar progressBar;
|
||||
private View emptyState;
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
private android.widget.ProgressBar progressBar;
|
||||
private android.view.View emptyState;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_favoritos);
|
||||
|
||||
View root = findViewById(R.id.favoritosRoot);
|
||||
if (root != null) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
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) {
|
||||
@@ -58,8 +54,8 @@ public class FavoritosActivity extends AppCompatActivity {
|
||||
|
||||
list = new ArrayList<>();
|
||||
adapter = new RestaurantAdapter(list, restaurant -> {
|
||||
Intent intent = new Intent(this, RestaurantDetailActivity.class);
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ID, restaurant.getId());
|
||||
android.content.Intent intent = new android.content.Intent(this, ExplorarRestaurantesActivity.class);
|
||||
intent.putExtra("restaurant", restaurant);
|
||||
startActivity(intent);
|
||||
});
|
||||
rv.setAdapter(adapter);
|
||||
@@ -69,26 +65,37 @@ public class FavoritosActivity extends AppCompatActivity {
|
||||
|
||||
private void setupFavoritesList() {
|
||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||
if (user != null) {
|
||||
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
db.collection("Clientes").document(user.getUid()).collection("favorites")
|
||||
.addSnapshotListener((value, error) -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
if (value == null) return;
|
||||
if (user != null && user.getEmail() != null) {
|
||||
String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_");
|
||||
DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("Clientes")
|
||||
.child(encodedUserEmail).child("favorites");
|
||||
|
||||
favRef.addValueEventListener(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
list.clear();
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
Restaurant restaurant = doc.toObject(Restaurant.class);
|
||||
restaurant.setId(doc.getId());
|
||||
list.add(restaurant);
|
||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||
Restaurant restaurant = ds.getValue(Restaurant.class);
|
||||
if (restaurant != null) {
|
||||
list.add(restaurant);
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if (emptyState != null) {
|
||||
emptyState.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
emptyState.setVisibility(list.isEmpty() ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
Toast.makeText(FavoritosActivity.this, "Erro ao carregar favoritos.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,17 @@ 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.firestore.DocumentReference;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
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 FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRestaurantAdapter.ViewHolder> {
|
||||
private List<Restaurant> restaurants;
|
||||
private RestaurantAdapter.OnRestaurantClickListener listener;
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
|
||||
public FeaturedRestaurantAdapter(List<Restaurant> restaurants,
|
||||
RestaurantAdapter.OnRestaurantClickListener listener) {
|
||||
@@ -43,17 +45,20 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
||||
|
||||
if (holder.txtRating != null) {
|
||||
if (restaurant.getRatingAvg() != null && restaurant.getRatingAvg() > 0) {
|
||||
holder.txtRating.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg()));
|
||||
holder.txtRating
|
||||
.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg()));
|
||||
} else {
|
||||
holder.txtRating.setText("Novo");
|
||||
}
|
||||
}
|
||||
|
||||
if (restaurant.getImageURL() != null && !restaurant.getImageURL().isEmpty()) {
|
||||
if (restaurant.getLogoUrl() != null && !restaurant.getLogoUrl().isEmpty()) {
|
||||
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||
.load(restaurant.getImageURL())
|
||||
.load(restaurant.getLogoUrl())
|
||||
.centerCrop()
|
||||
.into(holder.imgCover);
|
||||
} else {
|
||||
holder.imgCover.setImageResource(R.mipmap.ic_launcher);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
@@ -64,28 +69,49 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
||||
|
||||
if (holder.btnFavorite != null) {
|
||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||
if (user != null && restaurant.getId() != null) {
|
||||
DocumentReference favRef = db.collection("Clientes").document(user.getUid())
|
||||
.collection("favorites").document(restaurant.getId());
|
||||
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("Clientes")
|
||||
.child(encodedUserEmail).child("favorites").child(encodedRestEmail);
|
||||
|
||||
favRef.get().addOnSuccessListener(documentSnapshot -> {
|
||||
boolean exists = documentSnapshot.exists();
|
||||
holder.btnFavorite.setTag(exists);
|
||||
updateFavoriteUI(holder, exists);
|
||||
// Read initial state
|
||||
favRef.addListenerForSingleValueEvent(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
holder.btnFavorite.setTag(snapshot.exists());
|
||||
if (snapshot.exists()) {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
||||
} else {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||
holder.btnFavorite.clearColorFilter();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
}
|
||||
});
|
||||
|
||||
holder.btnFavorite.setOnClickListener(v -> {
|
||||
// Optimistic UI update
|
||||
boolean isFav = holder.btnFavorite.getTag() != null && (Boolean) holder.btnFavorite.getTag();
|
||||
boolean newFavState = !isFav;
|
||||
holder.btnFavorite.setTag(newFavState);
|
||||
updateFavoriteUI(holder, newFavState);
|
||||
|
||||
if (newFavState) {
|
||||
favRef.set(restaurant);
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
||||
favRef.setValue(restaurant);
|
||||
com.google.android.material.snackbar.Snackbar.make(v, "Adicionado aos Favoritos!",
|
||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||
} else {
|
||||
favRef.delete();
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||
holder.btnFavorite.clearColorFilter();
|
||||
favRef.removeValue();
|
||||
com.google.android.material.snackbar.Snackbar.make(v, "Removido dos Favoritos.",
|
||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
@@ -94,17 +120,6 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFavoriteUI(ViewHolder holder, boolean isFav) {
|
||||
if (isFav) {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
||||
} else {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||
holder.btnFavorite.clearColorFilter();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return restaurants.size();
|
||||
@@ -117,10 +132,10 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
txtName = itemView.findViewById(R.id.restaurantName);
|
||||
txtCategory = itemView.findViewById(R.id.restaurantCuisine);
|
||||
txtRating = null;
|
||||
imgCover = itemView.findViewById(R.id.restaurantImage);
|
||||
txtName = itemView.findViewById(R.id.txtFeaturedName);
|
||||
txtCategory = itemView.findViewById(R.id.txtFeaturedCategory);
|
||||
txtRating = itemView.findViewById(R.id.txtFeaturedRating);
|
||||
imgCover = itemView.findViewById(R.id.imgFeaturedCover);
|
||||
btnFavorite = itemView.findViewById(R.id.btnFavorite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.CollectionReference;
|
||||
|
||||
public class FirestoreManager {
|
||||
private static FirestoreManager instance;
|
||||
private final FirebaseFirestore db;
|
||||
|
||||
private FirestoreManager() {
|
||||
db = FirebaseFirestore.getInstance();
|
||||
}
|
||||
|
||||
public static synchronized FirestoreManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new FirestoreManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public FirebaseFirestore getDb() {
|
||||
return db;
|
||||
}
|
||||
|
||||
public CollectionReference getUsersCollection() {
|
||||
return db.collection("Clientes");
|
||||
}
|
||||
|
||||
public CollectionReference getRestaurantsCollection() {
|
||||
return db.collection("Restaurantes");
|
||||
}
|
||||
|
||||
public CollectionReference getReservationsCollection() {
|
||||
return db.collection("Reservas");
|
||||
}
|
||||
}
|
||||
@@ -38,12 +38,7 @@ public class FoodCategoryAdapter extends RecyclerView.Adapter<FoodCategoryAdapte
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
FoodCategory category = categories.get(position);
|
||||
holder.txtName.setText(category.getName());
|
||||
if (category.getImageUrl() != null && !category.getImageUrl().isEmpty()) {
|
||||
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||
.load(category.getImageUrl())
|
||||
.centerCrop()
|
||||
.into(holder.imgCategory);
|
||||
} else if (category.getImageResId() != 0) {
|
||||
if (category.getImageResId() != 0) {
|
||||
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||
.load(category.getImageResId())
|
||||
.centerCrop()
|
||||
|
||||
227
app/src/main/java/com/example/pap_teste/GerirMesasActivity.java
Normal file
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
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HomeFragment extends Fragment {
|
||||
|
||||
private RecyclerView rvFeatured, rvNearYou;
|
||||
private RestaurantAdapter featuredAdapter, nearYouAdapter;
|
||||
private final List<Restaurant> allRestaurants = new ArrayList<>();
|
||||
private final List<Restaurant> featuredList = new ArrayList<>();
|
||||
private final List<Restaurant> nearYouList = new ArrayList<>();
|
||||
private ChipGroup categoryChips;
|
||||
private EditText etSearch;
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_home, container, false);
|
||||
|
||||
rvFeatured = view.findViewById(R.id.rvFeatured);
|
||||
rvNearYou = view.findViewById(R.id.rvNearYou);
|
||||
categoryChips = view.findViewById(R.id.categoryChips);
|
||||
etSearch = view.findViewById(R.id.etSearch);
|
||||
|
||||
setupRecyclers();
|
||||
loadCategories();
|
||||
loadRestaurants();
|
||||
setupSearch();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void setupRecyclers() {
|
||||
featuredAdapter = new RestaurantAdapter(featuredList, true, this::onRestaurantClick);
|
||||
rvFeatured.setAdapter(featuredAdapter);
|
||||
rvFeatured.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
|
||||
nearYouAdapter = new RestaurantAdapter(nearYouList, false, this::onRestaurantClick);
|
||||
rvNearYou.setAdapter(nearYouAdapter);
|
||||
rvNearYou.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
}
|
||||
|
||||
private void loadCategories() {
|
||||
db.collection("Categorias").get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
categoryChips.removeAllViews();
|
||||
|
||||
// Add "Tudo" chip
|
||||
addCategoryChip("Tudo", true);
|
||||
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
String name = doc.getString("name");
|
||||
if (name != null) {
|
||||
addCategoryChip(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
categoryChips.setOnCheckedStateChangeListener((group, checkedIds) -> {
|
||||
if (checkedIds.isEmpty()) {
|
||||
filterRestaurants("Tudo");
|
||||
} else {
|
||||
Chip chip = group.findViewById(checkedIds.get(0));
|
||||
filterRestaurants(chip.getText().toString());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void addCategoryChip(String name, boolean isSelected) {
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.layout_filter_chip, categoryChips, false);
|
||||
chip.setText(name);
|
||||
chip.setChecked(isSelected);
|
||||
categoryChips.addView(chip);
|
||||
}
|
||||
|
||||
private void loadRestaurants() {
|
||||
FirestoreManager.getInstance().getRestaurantsCollection().get().addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful()) {
|
||||
allRestaurants.clear();
|
||||
for (QueryDocumentSnapshot document : task.getResult()) {
|
||||
Restaurant r = document.toObject(Restaurant.class);
|
||||
r.setId(document.getId());
|
||||
allRestaurants.add(r);
|
||||
}
|
||||
filterRestaurants("Tudo");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupSearch() {
|
||||
etSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
searchRestaurants(s.toString());
|
||||
}
|
||||
@Override public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void filterRestaurants(String category) {
|
||||
featuredList.clear();
|
||||
nearYouList.clear();
|
||||
|
||||
for (Restaurant r : allRestaurants) {
|
||||
if (category.equals("Tudo") || category.equalsIgnoreCase(r.getCategory())) {
|
||||
featuredList.add(r);
|
||||
nearYouList.add(r);
|
||||
}
|
||||
}
|
||||
featuredAdapter.notifyDataSetChanged();
|
||||
nearYouAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void searchRestaurants(String query) {
|
||||
featuredList.clear();
|
||||
nearYouList.clear();
|
||||
|
||||
for (Restaurant r : allRestaurants) {
|
||||
if (r.getName().toLowerCase().contains(query.toLowerCase()) ||
|
||||
r.getCategory().toLowerCase().contains(query.toLowerCase())) {
|
||||
featuredList.add(r);
|
||||
nearYouList.add(r);
|
||||
}
|
||||
}
|
||||
featuredAdapter.notifyDataSetChanged();
|
||||
nearYouAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void onRestaurantClick(Restaurant restaurant) {
|
||||
Intent intent = new Intent(getActivity(), RestaurantDetailActivity.class);
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ID, restaurant.getId());
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_NAME, restaurant.getName());
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_IMAGE, restaurant.getImageURL());
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_CUISINE, restaurant.getCategory());
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_RATING, restaurant.getRatingAvg());
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_DESC, restaurant.getDescription());
|
||||
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ADDRESS, restaurant.getAddress());
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
250
app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java
Normal file
250
app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java
Normal file
@@ -0,0 +1,250 @@
|
||||
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) {
|
||||
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
||||
restaurantEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
||||
} else {
|
||||
restaurantEmail = ""; // Avoid hardcoded sabor_arte so we don't accidentally load wrong data
|
||||
}
|
||||
}
|
||||
|
||||
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.getCapacidade() >= item.getPessoas()) {
|
||||
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 com capacidade suficiente para " + item.getPessoas() + " pessoas. 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.addValueEventListener(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
||||
reservasPendentes.clear();
|
||||
String targetEmail = restaurantEmail != null ? restaurantEmail.trim() : "";
|
||||
for (DataSnapshot data : snapshot.getChildren()) {
|
||||
Reserva r = data.getValue(Reserva.class);
|
||||
if (r != null && "Pendente".equals(r.getEstado())) {
|
||||
String rEmail = r.getRestauranteEmail();
|
||||
if (rEmail != null && rEmail.trim().equalsIgnoreCase(targetEmail)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,166 +1,594 @@
|
||||
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.ProgressBar;
|
||||
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.example.pap_teste.models.User;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
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";
|
||||
public static final String EXTRA_ACTION_MODE = "extra_action_mode";
|
||||
public enum AccountAction { CRIAR, ENTRAR }
|
||||
private static final String PREFS_NAME = "pap_prefs";
|
||||
private static final String KEY_HAS_CREATED_ACCOUNT = "has_created_account";
|
||||
|
||||
private FirebaseAuth mAuth;
|
||||
private FirebaseFirestore db;
|
||||
private boolean isLoginMode = true;
|
||||
public enum AccountType {
|
||||
CLIENTE, ESTABELECIMENTO
|
||||
}
|
||||
|
||||
private TextInputLayout layoutName;
|
||||
private TextInputEditText inputName, inputEmail, inputPassword;
|
||||
private MaterialButton btnPrimaryAction;
|
||||
private ProgressBar progressBar;
|
||||
public enum AccountAction {
|
||||
ENTRAR, CRIAR
|
||||
}
|
||||
|
||||
private AccountType selectedAccountType = AccountType.CLIENTE;
|
||||
private AccountAction selectedAccountAction = AccountAction.ENTRAR;
|
||||
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;
|
||||
});
|
||||
|
||||
mAuth = FirebaseAuth.getInstance();
|
||||
db = FirebaseFirestore.getInstance();
|
||||
progressBar = findViewById(R.id.progressBarLogin);
|
||||
selectedAccountType = AccountType.CLIENTE;
|
||||
|
||||
if (mAuth.getCurrentUser() != null) {
|
||||
checkUserRoleAndRedirect(mAuth.getCurrentUser().getUid());
|
||||
FirebaseApp.initializeApp(this);
|
||||
firebaseAuth = FirebaseAuth.getInstance();
|
||||
databaseReference = FirebaseDatabase.getInstance().getReference();
|
||||
|
||||
bindViews();
|
||||
setupActionToggle();
|
||||
setupPrimaryAction();
|
||||
checkPermissions();
|
||||
migrateUsersNodeToSeparateCollections();
|
||||
}
|
||||
|
||||
private void migrateUsersNodeToSeparateCollections() {
|
||||
if (databaseReference == null) return;
|
||||
databaseReference.child("users").get().addOnCompleteListener(task -> {
|
||||
if (task.isSuccessful() && task.getResult() != null && task.getResult().exists()) {
|
||||
for (DataSnapshot userSnapshot : task.getResult().getChildren()) {
|
||||
String accountType = userSnapshot.child("accountType").getValue(String.class);
|
||||
String role = userSnapshot.child("role").getValue(String.class);
|
||||
|
||||
String targetCollection = "Clientes";
|
||||
if ("ESTABELECIMENTO".equalsIgnoreCase(accountType) || "ADMIN".equalsIgnoreCase(role)) {
|
||||
targetCollection = "Restaurantes";
|
||||
}
|
||||
|
||||
databaseReference.child(targetCollection).child(userSnapshot.getKey())
|
||||
.setValue(userSnapshot.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
initViews();
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
permissionsNeeded.add(Manifest.permission.POST_NOTIFICATIONS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissionsNeeded.isEmpty()) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Permissões Necessárias")
|
||||
.setMessage(
|
||||
"Para o correto funcionamento do check-in, receber notificações de reservas e acesso a fotos, precisamos de algumas permissões.")
|
||||
.setPositiveButton("Configurar", (dialog, which) -> {
|
||||
permissionRequest.launch(permissionsNeeded.toArray(new String[0]));
|
||||
})
|
||||
.setNegativeButton("Agora Não", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
layoutName = findViewById(R.id.layoutName);
|
||||
private void bindViews() {
|
||||
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);
|
||||
btnPrimaryAction = findViewById(R.id.btnPrimaryAction);
|
||||
MaterialButtonToggleGroup toggleGroup = findViewById(R.id.toggleGroup);
|
||||
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);
|
||||
|
||||
toggleGroup.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
|
||||
if (isChecked) {
|
||||
isLoginMode = checkedId == R.id.btnEntrar;
|
||||
layoutName.setVisibility(isLoginMode ? View.GONE : View.VISIBLE);
|
||||
btnPrimaryAction.setText(isLoginMode ? "Entrar" : "Criar Conta");
|
||||
}
|
||||
});
|
||||
|
||||
btnPrimaryAction.setOnClickListener(v -> handleAuth());
|
||||
setupPasswordFeatures();
|
||||
}
|
||||
|
||||
private void checkUserRoleAndRedirect(String uid) {
|
||||
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
// 1. Try Clientes
|
||||
db.collection("Clientes").document(uid).get().addOnSuccessListener(docClient -> {
|
||||
if (docClient.exists()) {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||
finish();
|
||||
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 {
|
||||
// 2. Try legacy users collection
|
||||
db.collection("users").document(uid).get().addOnSuccessListener(docLegacy -> {
|
||||
if (docLegacy.exists()) {
|
||||
// Migrate to Clientes
|
||||
User legacyUser = docLegacy.toObject(User.class);
|
||||
if (legacyUser != null && "CLIENTE".equalsIgnoreCase(legacyUser.getRole())) {
|
||||
db.collection("Clientes").document(uid).set(legacyUser)
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||
finish();
|
||||
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();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
handleNoClientAccess(uid);
|
||||
}
|
||||
} else {
|
||||
handleNoClientAccess(uid);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).addOnFailureListener(e -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Erro de rede ou permissões. Verifique a sua ligação.", Toast.LENGTH_LONG).show();
|
||||
initViews();
|
||||
});
|
||||
initViews();
|
||||
}
|
||||
|
||||
private void handleNoClientAccess(String uid) {
|
||||
// Check if it's a Restaurant
|
||||
db.collection("Restaurantes").document(uid).get().addOnSuccessListener(docRest -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
if (docRest.exists()) {
|
||||
Toast.makeText(this, "Contas de Restaurante devem usar o Dashboard Web.", Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Toast.makeText(this, "Utilizador não encontrado. Por favor, crie uma conta.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
mAuth.signOut();
|
||||
initViews();
|
||||
})
|
||||
.setNegativeButton("Não", null)
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleAuth() {
|
||||
|
||||
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 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 name = inputName.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 todos os campos.", Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(this, "Preencha email e palavra-passe.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
||||
|
||||
if (isLoginMode) {
|
||||
mAuth.signInWithEmailAndPassword(email, password)
|
||||
.addOnSuccessListener(authResult -> checkUserRoleAndRedirect(authResult.getUser().getUid()))
|
||||
.addOnFailureListener(e -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Login falhou: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
});
|
||||
} else {
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Insira o seu nome.", Toast.LENGTH_SHORT).show();
|
||||
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;
|
||||
}
|
||||
mAuth.createUserWithEmailAndPassword(email, password)
|
||||
.addOnSuccessListener(authResult -> {
|
||||
String uid = authResult.getUser().getUid();
|
||||
User newUser = new User(uid, name, email, "CLIENTE");
|
||||
db.collection("Clientes").document(uid).set(newUser)
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||
finish();
|
||||
});
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Registo falhou: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
});
|
||||
|
||||
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();
|
||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
||||
return;
|
||||
}
|
||||
|
||||
String documentId = buildDocumentId(email);
|
||||
String collectionPath = selectedAccountType == AccountType.ESTABELECIMENTO ? "Restaurantes" : "Clientes";
|
||||
databaseReference.child(collectionPath).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. Tente novamente.", Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
||||
return;
|
||||
}
|
||||
|
||||
DataSnapshot snapshot = task.getResult();
|
||||
if (snapshot == null || !snapshot.exists()) {
|
||||
String tipo = selectedAccountType == AccountType.ESTABELECIMENTO ? "Estabelecimento" : "Cliente";
|
||||
Toast.makeText(this, "Conta não encontrada como " + tipo + ".\nPor favor, crie uma conta nova.", Toast.LENGTH_LONG).show();
|
||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
||||
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.\nPor favor verifique se escolheu a opção correta.", Toast.LENGTH_LONG)
|
||||
.show();
|
||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
||||
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);
|
||||
}
|
||||
|
||||
String collectionPath = selectedAccountType == AccountType.ESTABELECIMENTO ? "Restaurantes" : "Clientes";
|
||||
databaseReference.child(collectionPath).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;
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,11 @@ package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
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;
|
||||
@@ -15,11 +14,11 @@ import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.Query;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
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;
|
||||
@@ -29,36 +28,40 @@ public class MinhasReservasActivity extends AppCompatActivity {
|
||||
private RecyclerView rvMinhasReservas;
|
||||
private ReservaAdapter adapter;
|
||||
private final List<Reserva> reservaList = new ArrayList<>();
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
private DatabaseReference databaseReference;
|
||||
private String clientEmail;
|
||||
private ProgressBar progressBar;
|
||||
private View emptyState;
|
||||
private android.widget.ProgressBar progressBar;
|
||||
private android.view.View emptyState;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_minhas_reservas);
|
||||
|
||||
View root = findViewById(R.id.minhasReservasRoot);
|
||||
if (root != null) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
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;
|
||||
});
|
||||
|
||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||
if (user != null) {
|
||||
clientEmail = user.getEmail();
|
||||
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);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
emptyState = findViewById(R.id.emptyState);
|
||||
|
||||
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||
Button btnVoltar = findViewById(R.id.btnVoltar);
|
||||
btnVoltar.setOnClickListener(v -> finish());
|
||||
|
||||
setupAdapter();
|
||||
loadReservations();
|
||||
@@ -82,33 +85,64 @@ public class MinhasReservasActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void loadReservations() {
|
||||
if (clientEmail == null) return;
|
||||
databaseReference = FirebaseDatabase.getInstance().getReference("reservas");
|
||||
if (progressBar != null) progressBar.setVisibility(android.view.View.VISIBLE);
|
||||
if (emptyState != null) emptyState.setVisibility(android.view.View.GONE);
|
||||
|
||||
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
db.collection("Reservas").whereEqualTo("clienteEmail", clientEmail)
|
||||
.orderBy("createdAt", Query.Direction.DESCENDING)
|
||||
.addSnapshotListener((value, error) -> {
|
||||
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
if (value == null) return;
|
||||
|
||||
reservaList.clear();
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
Reserva r = doc.toObject(Reserva.class);
|
||||
r.setId(doc.getId());
|
||||
reservaList.add(r);
|
||||
databaseReference.orderByChild("clienteEmail").equalTo(clientEmail)
|
||||
.addValueEventListener(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
if (progressBar != null) progressBar.setVisibility(android.view.View.GONE);
|
||||
reservaList.clear();
|
||||
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
|
||||
Reserva reserva = dataSnapshot.getValue(Reserva.class);
|
||||
if (reserva != null) {
|
||||
reservaList.add(reserva);
|
||||
}
|
||||
}
|
||||
// Order reservations (newest first based on ID or we can just reverse the list)
|
||||
java.util.Collections.reverse(reservaList);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if (emptyState != null) {
|
||||
emptyState.setVisibility(reservaList.isEmpty() ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if (emptyState != null) {
|
||||
emptyState.setVisibility(reservaList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
if (progressBar != null) progressBar.setVisibility(android.view.View.GONE);
|
||||
Toast.makeText(MinhasReservasActivity.this, "Erro ao carregar reservas.", Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void cancelReservation(Reserva reserva) {
|
||||
if (reserva.getId() == null) return;
|
||||
db.collection("Reservas").document(reserva.getId()).update("estado", "Cancelada")
|
||||
.addOnSuccessListener(aVoid -> Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show());
|
||||
android.widget.EditText input = new android.widget.EditText(this);
|
||||
input.setHint("Motivo do cancelamento (opcional)");
|
||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("Cancelar Reserva")
|
||||
.setMessage("Deseja indicar o motivo do cancelamento?")
|
||||
.setView(input)
|
||||
.setPositiveButton("Confirmar", (dialog, which) -> {
|
||||
String motivo = input.getText().toString();
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
updates.put("estado", "Cancelada");
|
||||
if (!motivo.isEmpty()) {
|
||||
updates.put("motivo", motivo);
|
||||
}
|
||||
databaseReference.child(reserva.getId()).updateChildren(updates)
|
||||
.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();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton("Voltar", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.Query;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MyReservationsFragment extends Fragment {
|
||||
|
||||
private RecyclerView rvReservations;
|
||||
private ReservaAdapter adapter;
|
||||
private List<Reserva> reservationList = new ArrayList<>();
|
||||
private ProgressBar progressBar;
|
||||
private View emptyState;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_my_reservations, container, false);
|
||||
|
||||
rvReservations = view.findViewById(R.id.rvReservations);
|
||||
progressBar = view.findViewById(R.id.progressBar);
|
||||
emptyState = view.findViewById(R.id.emptyState);
|
||||
|
||||
setupAdapter();
|
||||
loadReservationsFromFirestore();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void setupAdapter() {
|
||||
adapter = new ReservaAdapter(reservationList, new ReservaAdapter.OnReservaActionListener() {
|
||||
@Override
|
||||
public void onCheckIn(Reserva reserva) {
|
||||
Toast.makeText(getContext(), "Funcionalidade de Check-in em breve", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Reserva reserva) {
|
||||
cancelReservation(reserva);
|
||||
}
|
||||
});
|
||||
rvReservations.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
rvReservations.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void cancelReservation(Reserva reserva) {
|
||||
if (reserva.getId() == null) return;
|
||||
FirestoreManager.getInstance().getReservationsCollection().document(reserva.getId())
|
||||
.update("estado", "Cancelada")
|
||||
.addOnSuccessListener(aVoid -> Toast.makeText(getContext(), "Reserva cancelada.", Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
private void loadReservationsFromFirestore() {
|
||||
String email = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||
? FirebaseAuth.getInstance().getCurrentUser().getEmail() : null;
|
||||
|
||||
if (email == null) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
emptyState.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
FirestoreManager.getInstance().getReservationsCollection()
|
||||
.whereEqualTo("clienteEmail", email)
|
||||
.orderBy("createdAt", Query.Direction.DESCENDING)
|
||||
.addSnapshotListener((value, error) -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
if (value == null) return;
|
||||
|
||||
reservationList.clear();
|
||||
for (QueryDocumentSnapshot doc : value) {
|
||||
Reserva r = doc.toObject(Reserva.class);
|
||||
r.setId(doc.getId());
|
||||
reservationList.add(r);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
emptyState.setVisibility(reservationList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,249 +1,436 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
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 androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.pap_teste.models.FoodCategory;
|
||||
import com.example.pap_teste.models.Mesa;
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import android.widget.Button;
|
||||
|
||||
public class NovaReservaActivity extends AppCompatActivity {
|
||||
|
||||
private enum State { CATEGORIES, RESTAURANTS, DETAILS }
|
||||
private enum State {
|
||||
CATEGORIES, RESTAURANTS, DETAILS
|
||||
}
|
||||
|
||||
private State currentState = State.CATEGORIES;
|
||||
private String selectedCategory = null;
|
||||
private Restaurant selectedRestaurant = null;
|
||||
private Mesa selectedMesa = null;
|
||||
private String selectedDate = null;
|
||||
private String selectedTime = null;
|
||||
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
||||
|
||||
private RecyclerView rvCategories, rvRestaurants, rvTables;
|
||||
private View scrollNovaReserva;
|
||||
private TextView txtTitle;
|
||||
private ProgressBar progressBar;
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
private androidx.recyclerview.widget.RecyclerView rvCategories, rvRestaurants;
|
||||
private android.view.View scrollNovaReserva;
|
||||
private android.widget.TextView txtTitle;
|
||||
private android.widget.ProgressBar progressBar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_nova_reserva);
|
||||
|
||||
View root = findViewById(R.id.novaReservaRoot);
|
||||
if (root != null) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
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;
|
||||
});
|
||||
|
||||
initViews();
|
||||
setupCategories();
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
rvCategories = findViewById(R.id.rvCategories);
|
||||
rvRestaurants = findViewById(R.id.rvRestaurants);
|
||||
rvTables = findViewById(R.id.rvTables);
|
||||
scrollNovaReserva = findViewById(R.id.scrollNovaReserva);
|
||||
txtTitle = findViewById(R.id.txtTituloNovaReserva);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
|
||||
findViewById(R.id.btnVoltar).setOnClickListener(v -> handleBackNavigation());
|
||||
findViewById(R.id.btnSelectDate).setOnClickListener(v -> showDatePicker());
|
||||
findViewById(R.id.btnSelectTime).setOnClickListener(v -> showTimePicker());
|
||||
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> validateAndSave());
|
||||
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();
|
||||
return;
|
||||
}
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
private void updateViewState() {
|
||||
rvCategories.setVisibility(currentState == State.CATEGORIES ? View.VISIBLE : View.GONE);
|
||||
rvRestaurants.setVisibility(currentState == State.RESTAURANTS ? View.VISIBLE : View.GONE);
|
||||
scrollNovaReserva.setVisibility(currentState == State.DETAILS ? View.VISIBLE : View.GONE);
|
||||
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);
|
||||
|
||||
switch (currentState) {
|
||||
case CATEGORIES: txtTitle.setText("Escolha o tema"); break;
|
||||
case RESTAURANTS: txtTitle.setText("Restaurantes: " + selectedCategory); break;
|
||||
case DETAILS: txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); break;
|
||||
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();
|
||||
|
||||
android.widget.TextView txtFechado = findViewById(R.id.txtRestauranteFechado);
|
||||
boolean isFechado = selectedRestaurant != null && selectedRestaurant.isFechadoWebsite();
|
||||
|
||||
if (txtFechado != null) {
|
||||
txtFechado.setVisibility(isFechado ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||
}
|
||||
|
||||
findViewById(R.id.btnSelectDate).setEnabled(!isFechado);
|
||||
findViewById(R.id.btnSelectTime).setEnabled(!isFechado);
|
||||
findViewById(R.id.etPartySize).setEnabled(!isFechado);
|
||||
findViewById(R.id.btnConfirmarReserva).setEnabled(!isFechado);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupCategories() {
|
||||
db.collection("Categorias").get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
List<FoodCategory> cats = new ArrayList<>();
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
FoodCategory cat = doc.toObject(FoodCategory.class);
|
||||
cats.add(cat);
|
||||
}
|
||||
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 LinearLayoutManager(this));
|
||||
rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> {
|
||||
selectedCategory = category.getName();
|
||||
currentState = State.RESTAURANTS;
|
||||
loadRestaurants();
|
||||
updateViewState();
|
||||
}));
|
||||
});
|
||||
rvCategories.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this));
|
||||
rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> {
|
||||
selectedCategory = category.getName();
|
||||
currentState = State.RESTAURANTS;
|
||||
setupRestaurants();
|
||||
updateViewState();
|
||||
}));
|
||||
}
|
||||
|
||||
private void loadRestaurants() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
db.collection("Restaurantes").whereEqualTo("category", selectedCategory).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
List<Restaurant> list = new ArrayList<>();
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
Restaurant r = doc.toObject(Restaurant.class);
|
||||
r.setId(doc.getId());
|
||||
list.add(r);
|
||||
}
|
||||
rvRestaurants.setLayoutManager(new LinearLayoutManager(this));
|
||||
rvRestaurants.setAdapter(new RestaurantAdapter(list, restaurant -> {
|
||||
selectedRestaurant = restaurant;
|
||||
currentState = State.DETAILS;
|
||||
loadTables();
|
||||
updateViewState();
|
||||
}));
|
||||
}).addOnFailureListener(e -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Erro ao carregar restaurantes.", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
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("Restaurantes");
|
||||
|
||||
private void loadTables() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
db.collection("Mesas").whereEqualTo("restauranteEmail", selectedRestaurant.getEmail()).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
List<Mesa> tables = new ArrayList<>();
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
Mesa m = doc.toObject(Mesa.class);
|
||||
m.setId(doc.getId());
|
||||
tables.add(m);
|
||||
}
|
||||
rvTables.setLayoutManager(new GridLayoutManager(this, 3));
|
||||
rvTables.setAdapter(new TableSelectionAdapter(tables, mesa -> selectedMesa = mesa));
|
||||
});
|
||||
}
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
||||
|
||||
private void showDatePicker() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new DatePickerDialog(this, (view, year, month, day) -> {
|
||||
selectedDate = String.format(Locale.getDefault(), "%02d/%02d/%d", day, month + 1, year);
|
||||
((Button)findViewById(R.id.btnSelectDate)).setText(selectedDate);
|
||||
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||
}
|
||||
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) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
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);
|
||||
|
||||
private void showTimePicker() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new TimePickerDialog(this, (view, hour, min) -> {
|
||||
selectedTime = String.format(Locale.getDefault(), "%02d:%02d", hour, min);
|
||||
((Button)findViewById(R.id.btnSelectTime)).setText(selectedTime);
|
||||
}, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
|
||||
}
|
||||
// Aceitar todos os registos na coleção Restaurantes
|
||||
if (true) {
|
||||
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);
|
||||
Boolean fechadoWebsite = ds.child("fechadoWebsite").getValue(Boolean.class);
|
||||
|
||||
private void validateAndSave() {
|
||||
EditText etPartySize = findViewById(R.id.etPartySize);
|
||||
String partySizeStr = etPartySize.getText().toString();
|
||||
|
||||
if (selectedDate == null || selectedTime == null || selectedMesa == null || partySizeStr.isEmpty()) {
|
||||
Toast.makeText(this, "Preencha todos os campos.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
int partySize = Integer.parseInt(partySizeStr);
|
||||
if (partySize > selectedMesa.getCapacidade()) {
|
||||
Toast.makeText(this, "A mesa só suporta " + selectedMesa.getCapacidade() + " pessoas.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
checkConflictAndSave(partySize);
|
||||
}
|
||||
|
||||
private void checkConflictAndSave(int guests) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
db.collection("Reservas")
|
||||
.whereEqualTo("restauranteEmail", selectedRestaurant.getEmail())
|
||||
.whereEqualTo("data", selectedDate)
|
||||
.whereEqualTo("hora", selectedTime)
|
||||
.get()
|
||||
.addOnSuccessListener(queryDocumentSnapshots -> {
|
||||
boolean conflict = false;
|
||||
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||
Long tableNum = doc.getLong("tableNumber");
|
||||
if (tableNum != null && tableNum == selectedMesa.getNumero()) {
|
||||
conflict = true;
|
||||
break;
|
||||
if (name != null && email != null) {
|
||||
com.example.pap_teste.models.Restaurant rest = new com.example.pap_teste.models.Restaurant(name, cat, email,
|
||||
false, logoUrl);
|
||||
if (fechadoWebsite != null) {
|
||||
rest.setFechadoWebsite(fechadoWebsite);
|
||||
}
|
||||
if (ds.hasChild("schedule")) {
|
||||
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||
for (com.google.firebase.database.DataSnapshot schDs : ds.child("schedule").getChildren()) {
|
||||
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||
schMap.put(schDs.getKey(), sd);
|
||||
}
|
||||
rest.setSchedule(schMap);
|
||||
} else if (ds.hasChild("operatingHours")) {
|
||||
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||
for (com.google.firebase.database.DataSnapshot schDs : ds.child("operatingHours").getChildren()) {
|
||||
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||
// Handle case where operatingHours has isOpen instead of closed
|
||||
if (schDs.hasChild("isOpen")) {
|
||||
Boolean isOpen = schDs.child("isOpen").getValue(Boolean.class);
|
||||
if (isOpen != null && sd != null) {
|
||||
sd.setClosed(!isOpen);
|
||||
}
|
||||
}
|
||||
schMap.put(schDs.getKey(), sd);
|
||||
}
|
||||
rest.setSchedule(schMap);
|
||||
} else if (ds.hasChild("horario")) {
|
||||
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||
for (com.google.firebase.database.DataSnapshot schDs : ds.child("horario").getChildren()) {
|
||||
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||
schMap.put(schDs.getKey(), sd);
|
||||
}
|
||||
rest.setSchedule(schMap);
|
||||
}
|
||||
filteredList.add(rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rvRestaurants.setLayoutManager(
|
||||
new androidx.recyclerview.widget.LinearLayoutManager(NovaReservaActivity.this));
|
||||
rvRestaurants.setAdapter(new RestaurantAdapter(filteredList, restaurant -> {
|
||||
selectedRestaurant = restaurant;
|
||||
currentState = State.DETAILS;
|
||||
updateViewState();
|
||||
}));
|
||||
}
|
||||
|
||||
if (conflict) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Esta mesa já está reservada.", Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
saveToFirestore(guests);
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
android.widget.Toast.makeText(NovaReservaActivity.this, "Erro ao carregar restaurantes.",
|
||||
android.widget.Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveToFirestore(int guests) {
|
||||
String userEmail = FirebaseAuth.getInstance().getCurrentUser() != null ? FirebaseAuth.getInstance().getCurrentUser().getEmail() : "anon@test.com";
|
||||
Reserva reserva = new Reserva(
|
||||
null,
|
||||
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();
|
||||
android.app.DatePickerDialog dpd = new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||
java.util.Calendar selectedCal = java.util.Calendar.getInstance();
|
||||
selectedCal.set(year, month, dayOfMonth);
|
||||
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||
|
||||
boolean isClosed = selectedRestaurant.isClosedForDay(dayOfWeek);
|
||||
|
||||
if (isClosed) {
|
||||
android.widget.Toast.makeText(this, "O restaurante encontra-se encerrado na data selecionada.", android.widget.Toast.LENGTH_LONG).show();
|
||||
selectedDate = null;
|
||||
btnDate.setText("Selecionar Data");
|
||||
btnTime.setEnabled(false);
|
||||
} else {
|
||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||
btnDate.setText(selectedDate);
|
||||
btnTime.setEnabled(true);
|
||||
}
|
||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH), cal.get(java.util.Calendar.DAY_OF_MONTH));
|
||||
dpd.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
|
||||
dpd.show();
|
||||
});
|
||||
|
||||
btnTime.setOnClickListener(v -> {
|
||||
if (selectedDate == null) {
|
||||
android.widget.Toast.makeText(this, "Por favor, selecione primeiro a data.", android.widget.Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||
String chosenTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||
|
||||
boolean isOutOfHours = false;
|
||||
if (selectedDate != null && selectedRestaurant.getSchedule() != null) {
|
||||
try {
|
||||
java.util.Date d = new java.text.SimpleDateFormat("d/M/yyyy", java.util.Locale.getDefault()).parse(selectedDate);
|
||||
java.util.Calendar selectedCal = java.util.Calendar.getInstance();
|
||||
if (d != null) selectedCal.setTime(d);
|
||||
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||
|
||||
com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getScheduleForDay(dayOfWeek);
|
||||
if (sd != null && !sd.isClosed()) {
|
||||
if (sd.getOpenTime() != null && sd.getCloseTime() != null && !sd.getOpenTime().isEmpty() && !sd.getCloseTime().isEmpty()) {
|
||||
String open = sd.getOpenTime();
|
||||
String close = sd.getCloseTime();
|
||||
if (open.compareTo(close) < 0) {
|
||||
if (chosenTime.compareTo(open) < 0 || chosenTime.compareTo(close) > 0) {
|
||||
isOutOfHours = true;
|
||||
}
|
||||
} else {
|
||||
if (chosenTime.compareTo(open) < 0 && chosenTime.compareTo(close) > 0) {
|
||||
isOutOfHours = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
if (isOutOfHours) {
|
||||
android.widget.Toast.makeText(this, "Por favor selecione uma hora dentro do horário de funcionamento.", android.widget.Toast.LENGTH_LONG).show();
|
||||
selectedTime = null;
|
||||
btnTime.setText("Selecionar Hora");
|
||||
} else {
|
||||
selectedTime = chosenTime;
|
||||
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();
|
||||
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
||||
android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
||||
if (btnConfirmar != null)
|
||||
btnConfirmar.setEnabled(false);
|
||||
|
||||
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 && m.getRestauranteEmail() != null && restEmail.trim().equalsIgnoreCase(m.getRestauranteEmail().trim())) {
|
||||
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.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 && r.getRestauranteEmail() != null &&
|
||||
r.getRestauranteEmail().trim().equalsIgnoreCase(restEmail.trim()) &&
|
||||
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,
|
||||
guests,
|
||||
"Pendente"
|
||||
);
|
||||
reserva.setTableNumber(selectedMesa.getNumero());
|
||||
partySize,
|
||||
"Pendente");
|
||||
|
||||
db.collection("Reservas").add(reserva).addOnSuccessListener(documentReference -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Reserva realizada com sucesso!", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}).addOnFailureListener(e -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(this, "Erro ao guardar reserva.", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
if (id != null) {
|
||||
ref.child(id).setValue(reserva).addOnCompleteListener(task -> {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
||||
if (btnConfirmar != null)
|
||||
btnConfirmar.setEnabled(true);
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
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 com.google.android.material.button.MaterialButton;
|
||||
|
||||
public class OnboardingActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_onboarding);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(android.R.id.content), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
ImageView logo = findViewById(R.id.logo);
|
||||
TextView tagline = findViewById(R.id.tagline);
|
||||
MaterialButton btnGetStarted = findViewById(R.id.btnGetStarted);
|
||||
|
||||
// Simple animation
|
||||
Animation fadeIn = new AlphaAnimation(0, 1);
|
||||
fadeIn.setDuration(1500);
|
||||
logo.startAnimation(fadeIn);
|
||||
tagline.startAnimation(fadeIn);
|
||||
|
||||
btnGetStarted.setOnClickListener(v -> {
|
||||
android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
startActivity(new Intent(OnboardingActivity.this, MainActivity.class), options.toBundle());
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,60 +3,71 @@ package com.example.pap_teste;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
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.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
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 com.google.firebase.auth.FirebaseAuth;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ProfileDashboardActivity extends AppCompatActivity {
|
||||
|
||||
private EditText inputName, inputPhone, inputEmailEdit;
|
||||
private String photoUrl;
|
||||
private String email, documentId, photoUrl;
|
||||
private DatabaseReference databaseReference;
|
||||
private ImageView imgProfile;
|
||||
private androidx.activity.result.ActivityResultLauncher<Intent> imagePickerLauncher;
|
||||
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||
private final FirebaseAuth mAuth = FirebaseAuth.getInstance();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_profile_dashboard);
|
||||
|
||||
View root = findViewById(R.id.profileRoot);
|
||||
if (root != null) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
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("Clientes");
|
||||
|
||||
inputName = findViewById(R.id.inputProfileName);
|
||||
inputPhone = findViewById(R.id.inputProfilePhone);
|
||||
inputEmailEdit = findViewById(R.id.inputProfileEmail);
|
||||
imgProfile = findViewById(R.id.imgProfile);
|
||||
|
||||
fetchProfileData();
|
||||
if (currentName != null) {
|
||||
inputName.setText(currentName);
|
||||
}
|
||||
if (email != null) {
|
||||
inputEmailEdit.setText(email);
|
||||
}
|
||||
|
||||
fetchAdditionalProfileData();
|
||||
|
||||
imagePickerLauncher = registerForActivityResult(
|
||||
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
||||
@@ -71,7 +82,7 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
||||
|
||||
findViewById(R.id.cardProfileBig).setOnClickListener(v -> {
|
||||
String[] options = {"Galeria", "URL da Imagem"};
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
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) {
|
||||
@@ -85,48 +96,80 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
||||
builder.show();
|
||||
});
|
||||
|
||||
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||
findViewById(R.id.btnSaveProfile).setOnClickListener(v -> saveProfile());
|
||||
findViewById(R.id.btnLogOut).setOnClickListener(v -> performLogOut());
|
||||
findViewById(R.id.btnFavoritos).setOnClickListener(v -> startActivity(new Intent(this, FavoritosActivity.class)));
|
||||
findViewById(R.id.btnMinhasReservas).setOnClickListener(v -> startActivity(new Intent(this, MinhasReservasActivity.class)));
|
||||
Button btnSave = findViewById(R.id.btnSaveProfile);
|
||||
Button btnBack = findViewById(R.id.btnVoltar);
|
||||
Button btnFavs = findViewById(R.id.btnFavoritos);
|
||||
Button btnRes = findViewById(R.id.btnMinhasReservas);
|
||||
Button btnLogOut = findViewById(R.id.btnLogOut);
|
||||
|
||||
btnBack.setOnClickListener(v -> finish());
|
||||
btnSave.setOnClickListener(v -> saveProfile());
|
||||
|
||||
btnLogOut.setOnClickListener(v -> performLogOut());
|
||||
|
||||
btnFavs.setOnClickListener(v -> {
|
||||
startActivity(new Intent(this, FavoritosActivity.class));
|
||||
});
|
||||
|
||||
btnRes.setOnClickListener(v -> {
|
||||
startActivity(new Intent(this, MinhasReservasActivity.class));
|
||||
});
|
||||
}
|
||||
|
||||
private void performLogOut() {
|
||||
mAuth.signOut();
|
||||
Toast.makeText(this, "Sessão terminada.", Toast.LENGTH_SHORT).show();
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
try {
|
||||
stopService(new Intent(this, ReservationNotificationService.class));
|
||||
FirebaseAuth.getInstance().signOut();
|
||||
Toast.makeText(this, "Sessão terminada com sucesso.", Toast.LENGTH_SHORT).show();
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, "Erro ao terminar sessão: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
android.util.Log.e("LogOut", "Error during sign out", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void showUrlInputDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
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) -> {
|
||||
photoUrl = input.getText().toString().trim();
|
||||
if (!TextUtils.isEmpty(photoUrl)) {
|
||||
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", null);
|
||||
builder.setNegativeButton("Cancelar", (dialog, which) -> dialog.cancel());
|
||||
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void fetchProfileData() {
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
if (user == null) return;
|
||||
|
||||
db.collection("Clientes").document(user.getUid()).get().addOnSuccessListener(snapshot -> {
|
||||
private void fetchAdditionalProfileData() {
|
||||
if (documentId == null)
|
||||
return;
|
||||
databaseReference.child(documentId).get().addOnSuccessListener(snapshot -> {
|
||||
if (snapshot.exists()) {
|
||||
inputName.setText(snapshot.getString("displayName"));
|
||||
inputPhone.setText(snapshot.getString("phone"));
|
||||
inputEmailEdit.setText(snapshot.getString("email"));
|
||||
photoUrl = snapshot.getString("photoUrl");
|
||||
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);
|
||||
}
|
||||
@@ -134,36 +177,65 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void uploadImageToFirebase(android.net.Uri imageUri) {
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
if (user == null) return;
|
||||
private void uploadImageToFirebase(Uri imageUri) {
|
||||
if (documentId == null)
|
||||
return;
|
||||
|
||||
StorageReference storageRef = FirebaseStorage.getInstance().getReference()
|
||||
.child("profile_pics/" + user.getUid());
|
||||
.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);
|
||||
db.collection("Clientes").document(user.getUid()).update("photoUrl", photoUrl);
|
||||
|
||||
// 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() {
|
||||
FirebaseUser user = mAuth.getCurrentUser();
|
||||
if (user == null) return;
|
||||
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", inputName.getText().toString().trim());
|
||||
updates.put("phone", inputPhone.getText().toString().trim());
|
||||
updates.put("email", inputEmailEdit.getText().toString().trim());
|
||||
if (photoUrl != null) updates.put("photoUrl", photoUrl);
|
||||
updates.put("displayName", newName);
|
||||
updates.put("phone", newPhone);
|
||||
updates.put("email", newEmail);
|
||||
if (photoUrl != null) {
|
||||
updates.put("photoUrl", photoUrl);
|
||||
}
|
||||
|
||||
db.collection("Clientes").document(user.getUid()).update(updates)
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
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.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.widget.Toast;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
|
||||
public class ProfileFragment extends Fragment {
|
||||
|
||||
private ImageView imgProfile;
|
||||
private TextView txtName, txtEmail;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||
|
||||
imgProfile = view.findViewById(R.id.imgProfile);
|
||||
txtName = view.findViewById(R.id.txtProfileName);
|
||||
txtEmail = view.findViewById(R.id.txtProfileEmail);
|
||||
|
||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||
if (user != null) {
|
||||
txtName.setText(user.getDisplayName() != null ? user.getDisplayName() : "Utilizador");
|
||||
txtEmail.setText(user.getEmail());
|
||||
|
||||
if (user.getPhotoUrl() != null) {
|
||||
Glide.with(this).load(user.getPhotoUrl()).circleCrop().into(imgProfile);
|
||||
}
|
||||
}
|
||||
|
||||
view.findViewById(R.id.btnLogout).setOnClickListener(v -> {
|
||||
FirebaseAuth.getInstance().signOut();
|
||||
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -5,24 +5,24 @@ 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 com.google.android.material.chip.Chip;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
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;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
|
||||
|
||||
public ReservaAdapter(List<Reserva> reservas, OnReservaActionListener listener) {
|
||||
this.reservas = reservas;
|
||||
@@ -39,40 +39,64 @@ public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHold
|
||||
@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.txtRestaurante.setText(reserva.getRestaurantName());
|
||||
|
||||
String dateStr = reserva.getDate() != null ? dateFormat.format(reserva.getDate().toDate()) : "Data N/A";
|
||||
holder.txtDataHora.setText(String.format("%s às %s • %d pessoas", dateStr, reserva.getTimeSlot(), reserva.getGuests()));
|
||||
|
||||
String status = reserva.getStatus() != null ? reserva.getStatus() : "pending";
|
||||
holder.chipStatus.setText(status.toUpperCase());
|
||||
|
||||
// Status Colors
|
||||
int colorBg, colorText;
|
||||
android.content.Context ctx = holder.itemView.getContext();
|
||||
if ("confirmed".equals(status)) {
|
||||
colorBg = ctx.getColor(R.color.colorChipConfirmed);
|
||||
colorText = ctx.getColor(R.color.colorChipConfirmedText);
|
||||
holder.btnCheckIn.setVisibility(View.VISIBLE);
|
||||
} else if ("cancelled".equals(status) || "rejected".equals(status)) {
|
||||
colorBg = ctx.getColor(R.color.colorChipCancelled);
|
||||
colorText = ctx.getColor(R.color.colorChipCancelledText);
|
||||
holder.btnCheckIn.setVisibility(View.GONE);
|
||||
} else {
|
||||
colorBg = ctx.getColor(R.color.colorChipPending);
|
||||
colorText = ctx.getColor(R.color.colorChipPendingText);
|
||||
holder.btnCheckIn.setVisibility(View.GONE);
|
||||
if (holder.chipStatus != null) {
|
||||
String state = reserva.getEstado() != null ? reserva.getEstado() : "Pendente";
|
||||
holder.chipStatus.setText(state);
|
||||
int colorBg, colorText;
|
||||
android.content.Context ctx = holder.itemView.getContext();
|
||||
if ("Confirmada".equals(state)) {
|
||||
colorBg = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipConfirmed);
|
||||
colorText = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipConfirmedText);
|
||||
} else if ("Cancelada".equals(state) || "Recusada".equals(state)) {
|
||||
colorBg = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipCancelled);
|
||||
colorText = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipCancelledText);
|
||||
} else {
|
||||
colorBg = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipPending);
|
||||
colorText = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipPendingText);
|
||||
}
|
||||
holder.chipStatus.setChipBackgroundColor(android.content.res.ColorStateList.valueOf(colorBg));
|
||||
holder.chipStatus.setTextColor(colorText);
|
||||
}
|
||||
|
||||
holder.chipStatus.setChipBackgroundColor(android.content.res.ColorStateList.valueOf(colorBg));
|
||||
holder.chipStatus.setTextColor(colorText);
|
||||
|
||||
boolean canCancel = "pending".equals(status) || "confirmed".equals(status);
|
||||
// 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 -> listener.onCheckIn(reserva));
|
||||
holder.btnCancelar.setOnClickListener(v -> listener.onCancel(reserva));
|
||||
holder.btnCheckIn.setOnClickListener(v -> {
|
||||
if (listener != null)
|
||||
listener.onCheckIn(reserva);
|
||||
});
|
||||
|
||||
holder.btnCancelar.setOnClickListener(v -> {
|
||||
if (listener != null)
|
||||
listener.onCancel(reserva);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,18 +105,19 @@ public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHold
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView txtRestaurante, txtDataHora;
|
||||
TextView txtRestaurante, txtDataHora, txtStatus, txtLocationWarning;
|
||||
Button btnCheckIn, btnCancelar;
|
||||
Chip chipStatus;
|
||||
com.google.android.material.chip.Chip chipStatus;
|
||||
|
||||
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);
|
||||
chipStatus = itemView.findViewById(R.id.chipReservaStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.google.firebase.Timestamp;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class ReservationActivity extends AppCompatActivity {
|
||||
|
||||
private String restaurantId, restaurantName;
|
||||
private int guestCount = 2;
|
||||
private Calendar selectedDate = Calendar.getInstance();
|
||||
private String selectedTimeSlot = null;
|
||||
|
||||
private TextView txtGuestCount, txtSelectedDate;
|
||||
private RecyclerView rvTimeSlots;
|
||||
private TimeSlotAdapter adapter;
|
||||
private List<String> timeSlots = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_reservation);
|
||||
|
||||
restaurantId = getIntent().getStringExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ID);
|
||||
restaurantName = getIntent().getStringExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_NAME);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle("Reservar: " + restaurantName);
|
||||
}
|
||||
|
||||
txtGuestCount = findViewById(R.id.txtGuestCount);
|
||||
txtSelectedDate = findViewById(R.id.txtSelectedDate);
|
||||
rvTimeSlots = findViewById(R.id.rvTimeSlots);
|
||||
EditText etSpecialRequests = findViewById(R.id.etSpecialRequests);
|
||||
|
||||
findViewById(R.id.btnDecreaseGuests).setOnClickListener(v -> {
|
||||
if (guestCount > 1) {
|
||||
guestCount--;
|
||||
txtGuestCount.setText(String.valueOf(guestCount));
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.btnIncreaseGuests).setOnClickListener(v -> {
|
||||
guestCount++;
|
||||
txtGuestCount.setText(String.valueOf(guestCount));
|
||||
});
|
||||
|
||||
findViewById(R.id.cardDatePicker).setOnClickListener(v -> showDatePicker());
|
||||
|
||||
setupTimeSlots();
|
||||
loadRestaurantSlots();
|
||||
|
||||
findViewById(R.id.btnConfirmReservation).setOnClickListener(v -> {
|
||||
if (selectedTimeSlot == null) {
|
||||
Toast.makeText(this, "Por favor, selecione um horário.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
confirmReservation(etSpecialRequests.getText().toString());
|
||||
});
|
||||
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
}
|
||||
|
||||
private void showDatePicker() {
|
||||
new DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||
selectedDate.set(year, month, dayOfMonth);
|
||||
txtSelectedDate.setText(String.format("%02d/%02d/%d", dayOfMonth, month + 1, year));
|
||||
}, selectedDate.get(Calendar.YEAR), selectedDate.get(Calendar.MONTH), selectedDate.get(Calendar.DAY_OF_MONTH)).show();
|
||||
}
|
||||
|
||||
private void setupTimeSlots() {
|
||||
adapter = new TimeSlotAdapter(timeSlots, slot -> selectedTimeSlot = slot);
|
||||
rvTimeSlots.setAdapter(adapter);
|
||||
rvTimeSlots.setLayoutManager(new GridLayoutManager(this, 3));
|
||||
}
|
||||
|
||||
private void loadRestaurantSlots() {
|
||||
// In a real app, we would fetch from Firestore based on the selected date
|
||||
// For now, let's use the static slots from the restaurant model
|
||||
FirebaseFirestore.getInstance().collection("restaurants").document(restaurantId).get()
|
||||
.addOnSuccessListener(documentSnapshot -> {
|
||||
Restaurant r = documentSnapshot.toObject(Restaurant.class);
|
||||
if (r != null && r.getAvailableSlots() != null) {
|
||||
timeSlots.clear();
|
||||
timeSlots.addAll(r.getAvailableSlots());
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void confirmReservation(String specialRequests) {
|
||||
String userId = FirebaseAuth.getInstance().getUid();
|
||||
if (userId == null) {
|
||||
Toast.makeText(this, "Erro de autenticação.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Reserva reserva = new Reserva();
|
||||
reserva.setUserId(userId);
|
||||
reserva.setRestaurantId(restaurantId);
|
||||
reserva.setRestaurantName(restaurantName);
|
||||
reserva.setGuests(guestCount);
|
||||
reserva.setDate(new Timestamp(selectedDate.getTime()));
|
||||
reserva.setTimeSlot(selectedTimeSlot);
|
||||
reserva.setSpecialRequests(specialRequests);
|
||||
reserva.setStatus("confirmed");
|
||||
reserva.setCreatedAt(new Timestamp(new Date()));
|
||||
|
||||
FirebaseFirestore.getInstance().collection("reservations").add(reserva)
|
||||
.addOnSuccessListener(documentReference -> {
|
||||
Toast.makeText(this, "Reserva confirmada com sucesso!", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
})
|
||||
.addOnFailureListener(e -> Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.database.ChildEventListener;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
|
||||
public class ReservationNotificationService extends Service {
|
||||
|
||||
private static final String CHANNEL_ID = "ReservaNotifications";
|
||||
private DatabaseReference reservasRef;
|
||||
private ChildEventListener childEventListener;
|
||||
private String currentUserEmail;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||
if (user != null && user.getEmail() != null) {
|
||||
currentUserEmail = user.getEmail();
|
||||
startListeningForReservations();
|
||||
} else {
|
||||
stopSelf();
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void startListeningForReservations() {
|
||||
if (reservasRef != null && childEventListener != null) {
|
||||
return; // Already listening
|
||||
}
|
||||
|
||||
reservasRef = FirebaseDatabase.getInstance().getReference("reservas");
|
||||
childEventListener = new ChildEventListener() {
|
||||
@Override
|
||||
public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
|
||||
// Not needed for new reservations created by the user
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
|
||||
Reserva reserva = snapshot.getValue(Reserva.class);
|
||||
if (reserva != null && currentUserEmail.equals(reserva.getClienteEmail())) {
|
||||
String estado = reserva.getEstado();
|
||||
if (estado != null && (estado.startsWith("Confirmada") || estado.equals("Recusada") || estado.equals("Concluída") || estado.equals("Cancelada"))) {
|
||||
sendNotification(reserva);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildRemoved(@NonNull DataSnapshot snapshot) {}
|
||||
|
||||
@Override
|
||||
public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {}
|
||||
};
|
||||
|
||||
reservasRef.addChildEventListener(childEventListener);
|
||||
}
|
||||
|
||||
private void sendNotification(Reserva reserva) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||
ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
return; // Permission not granted
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class); // Or deep link to reservations
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
String title = "Atualização de Reserva";
|
||||
String message = "A sua reserva no " + reserva.getRestauranteName() + " foi " + reserva.getEstado().toLowerCase() + ".";
|
||||
|
||||
if (reserva.getMotivo() != null && !reserva.getMotivo().isEmpty()) {
|
||||
message += " Motivo: " + reserva.getMotivo();
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.na_mesa) // Assuming na_mesa is a valid drawable
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
|
||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.notify(reserva.getId().hashCode(), builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
CharSequence name = "Notificações de Reservas";
|
||||
String description = "Notificações sobre o estado das suas reservas";
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
|
||||
channel.setDescription(description);
|
||||
|
||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (reservasRef != null && childEventListener != null) {
|
||||
reservasRef.removeEventListener(childEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3,54 +3,132 @@ package com.example.pap_teste;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
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 final List<Restaurant> restaurants;
|
||||
private final boolean isFeatured;
|
||||
private final OnRestaurantClickListener listener;
|
||||
private List<Restaurant> restaurants;
|
||||
private OnRestaurantClickListener listener;
|
||||
|
||||
public interface OnRestaurantClickListener {
|
||||
void onRestaurantClick(Restaurant restaurant);
|
||||
}
|
||||
|
||||
public RestaurantAdapter(List<Restaurant> restaurants, OnRestaurantClickListener listener) {
|
||||
this(restaurants, false, listener);
|
||||
}
|
||||
|
||||
public RestaurantAdapter(List<Restaurant> restaurants, boolean isFeatured, OnRestaurantClickListener listener) {
|
||||
this.restaurants = restaurants;
|
||||
this.isFeatured = isFeatured;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
int layoutId = isFeatured ? R.layout.item_restaurant_featured : R.layout.item_restaurant_card;
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
|
||||
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.name.setText(restaurant.getName());
|
||||
holder.cuisine.setText(String.format("%s • %.1f ★", restaurant.getCuisine(), restaurant.getRating()));
|
||||
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.load(restaurant.getImageURL())
|
||||
.placeholder(R.drawable.na_mesa)
|
||||
.into(holder.image);
|
||||
holder.text1.setText(restaurant.getName());
|
||||
holder.text2
|
||||
.setText(restaurant.getCategory() + (restaurant.isAvailable() ? " - Disponível" : " - Indisponível"));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> listener.onRestaurantClick(restaurant));
|
||||
if (holder.txtRating != null) {
|
||||
if (restaurant.getRatingAvg() != null && restaurant.getRatingAvg() > 0) {
|
||||
holder.txtRating
|
||||
.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg()));
|
||||
} else {
|
||||
holder.txtRating.setText("Novo");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
if (holder.btnReservar != null) {
|
||||
holder.btnReservar.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("Clientes")
|
||||
.child(encodedUserEmail).child("favorites").child(encodedRestEmail);
|
||||
|
||||
// Read initial state
|
||||
favRef.addListenerForSingleValueEvent(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
holder.btnFavorite.setTag(snapshot.exists());
|
||||
if (snapshot.exists()) {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
||||
} else {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||
holder.btnFavorite.clearColorFilter();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
}
|
||||
});
|
||||
|
||||
holder.btnFavorite.setOnClickListener(v -> {
|
||||
// Optimistic UI update
|
||||
boolean isFav = holder.btnFavorite.getTag() != null && (Boolean) holder.btnFavorite.getTag();
|
||||
boolean newFavState = !isFav;
|
||||
holder.btnFavorite.setTag(newFavState);
|
||||
|
||||
if (newFavState) {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
||||
favRef.setValue(restaurant);
|
||||
com.google.android.material.snackbar.Snackbar.make(v, "Adicionado aos Favoritos!",
|
||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||
} else {
|
||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||
holder.btnFavorite.clearColorFilter();
|
||||
favRef.removeValue();
|
||||
com.google.android.material.snackbar.Snackbar.make(v, "Removido dos Favoritos.",
|
||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,16 +136,20 @@ public class RestaurantAdapter extends RecyclerView.Adapter<RestaurantAdapter.Vi
|
||||
return restaurants.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView image;
|
||||
TextView name;
|
||||
TextView cuisine;
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView text1, text2, txtRating;
|
||||
ImageButton btnFavorite;
|
||||
android.widget.ImageView imgThumb;
|
||||
Button btnReservar;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
image = itemView.findViewById(R.id.restaurantImage);
|
||||
name = itemView.findViewById(R.id.restaurantName);
|
||||
cuisine = itemView.findViewById(R.id.restaurantCuisine);
|
||||
text1 = itemView.findViewById(R.id.txtRestaurantName);
|
||||
text2 = itemView.findViewById(R.id.txtRestaurantCategory);
|
||||
txtRating = itemView.findViewById(R.id.txtRestaurantRating);
|
||||
btnFavorite = itemView.findViewById(R.id.btnFavorite);
|
||||
imgThumb = itemView.findViewById(R.id.imgRestaurantThumb);
|
||||
btnReservar = itemView.findViewById(R.id.btnReservarCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
public class RestaurantDetailActivity extends AppCompatActivity {
|
||||
|
||||
public static final String EXTRA_RESTAURANT_ID = "extra_restaurant_id";
|
||||
public static final String EXTRA_RESTAURANT_NAME = "extra_restaurant_name";
|
||||
public static final String EXTRA_RESTAURANT_IMAGE = "extra_restaurant_image";
|
||||
public static final String EXTRA_RESTAURANT_CUISINE = "extra_restaurant_cuisine";
|
||||
public static final String EXTRA_RESTAURANT_RATING = "extra_restaurant_rating";
|
||||
public static final String EXTRA_RESTAURANT_DESC = "extra_restaurant_desc";
|
||||
public static final String EXTRA_RESTAURANT_ADDRESS = "extra_restaurant_address";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_restaurant_detail);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle("");
|
||||
}
|
||||
|
||||
ImageView coverImage = findViewById(R.id.restaurantCover);
|
||||
TextView nameText = findViewById(R.id.detailName);
|
||||
TextView cuisineText = findViewById(R.id.detailCuisine);
|
||||
TextView ratingText = findViewById(R.id.detailRating);
|
||||
TextView descText = findViewById(R.id.detailDescription);
|
||||
TextView addressText = findViewById(R.id.detailAddress);
|
||||
MaterialButton btnReserve = findViewById(R.id.btnReserveNow);
|
||||
|
||||
// Get data from intent
|
||||
String name = getIntent().getStringExtra(EXTRA_RESTAURANT_NAME);
|
||||
String image = getIntent().getStringExtra(EXTRA_RESTAURANT_IMAGE);
|
||||
String cuisine = getIntent().getStringExtra(EXTRA_RESTAURANT_CUISINE);
|
||||
double rating = getIntent().getDoubleExtra(EXTRA_RESTAURANT_RATING, 0.0);
|
||||
String desc = getIntent().getStringExtra(EXTRA_RESTAURANT_DESC);
|
||||
String address = getIntent().getStringExtra(EXTRA_RESTAURANT_ADDRESS);
|
||||
|
||||
nameText.setText(name);
|
||||
cuisineText.setText(cuisine);
|
||||
ratingText.setText(String.format("%.1f ★", rating));
|
||||
descText.setText(desc);
|
||||
addressText.setText(address);
|
||||
|
||||
Glide.with(this).load(image).into(coverImage);
|
||||
|
||||
btnReserve.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, ReservationActivity.class);
|
||||
intent.putExtra(EXTRA_RESTAURANT_ID, getIntent().getStringExtra(EXTRA_RESTAURANT_ID));
|
||||
intent.putExtra(EXTRA_RESTAURANT_NAME, getIntent().getStringExtra(EXTRA_RESTAURANT_NAME));
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_splash);
|
||||
|
||||
ImageView logo = findViewById(R.id.splash_logo);
|
||||
TextView title = findViewById(R.id.splash_title);
|
||||
TextView subtitle = findViewById(R.id.splash_subtitle);
|
||||
|
||||
Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
|
||||
fadeIn.setDuration(1500);
|
||||
|
||||
logo.startAnimation(fadeIn);
|
||||
title.startAnimation(fadeIn);
|
||||
subtitle.startAnimation(fadeIn);
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
startActivity(new Intent(SplashActivity.this, OnboardingActivity.class));
|
||||
finish();
|
||||
}, 2500);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.graphics.Color;
|
||||
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.Mesa;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import java.util.List;
|
||||
|
||||
public class TableSelectionAdapter extends RecyclerView.Adapter<TableSelectionAdapter.ViewHolder> {
|
||||
|
||||
private final List<Mesa> tables;
|
||||
private final OnTableClickListener listener;
|
||||
private int selectedPosition = -1;
|
||||
|
||||
public interface OnTableClickListener {
|
||||
void onTableClick(Mesa mesa);
|
||||
}
|
||||
|
||||
public TableSelectionAdapter(List<Mesa> tables, OnTableClickListener listener) {
|
||||
this.tables = tables;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_table, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Mesa mesa = tables.get(position);
|
||||
holder.txtNumber.setText("Mesa " + mesa.getNumero());
|
||||
|
||||
boolean isOccupied = "OCUPADA".equalsIgnoreCase(mesa.getEstado()) || "RESERVADA".equalsIgnoreCase(mesa.getEstado());
|
||||
holder.txtStatus.setText(mesa.getEstado());
|
||||
|
||||
if (isOccupied) {
|
||||
holder.card.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
|
||||
holder.card.setStrokeColor(Color.TRANSPARENT);
|
||||
holder.txtStatus.setTextColor(Color.GRAY);
|
||||
holder.itemView.setEnabled(false);
|
||||
holder.imgStatus.setAlpha(0.3f);
|
||||
} else {
|
||||
boolean isSelected = selectedPosition == position;
|
||||
holder.card.setCardBackgroundColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorChipConfirmed) : Color.WHITE);
|
||||
holder.card.setStrokeColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorBorder));
|
||||
holder.txtStatus.setTextColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorTextSecondary));
|
||||
holder.itemView.setEnabled(true);
|
||||
holder.imgStatus.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
int previousSelected = selectedPosition;
|
||||
selectedPosition = holder.getAdapterPosition();
|
||||
if (previousSelected != -1) notifyItemChanged(previousSelected);
|
||||
notifyItemChanged(selectedPosition);
|
||||
listener.onTableClick(mesa);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return tables.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
MaterialCardView card;
|
||||
TextView txtNumber, txtStatus;
|
||||
ImageView imgStatus;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
card = itemView.findViewById(R.id.cardTable);
|
||||
txtNumber = itemView.findViewById(R.id.txtTableNumber);
|
||||
txtStatus = itemView.findViewById(R.id.txtTableStatus);
|
||||
imgStatus = itemView.findViewById(R.id.imgTableStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.graphics.Color;
|
||||
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 com.google.android.material.card.MaterialCardView;
|
||||
import java.util.List;
|
||||
|
||||
public class TimeSlotAdapter extends RecyclerView.Adapter<TimeSlotAdapter.ViewHolder> {
|
||||
|
||||
private final List<String> timeSlots;
|
||||
private final OnTimeSlotClickListener listener;
|
||||
private int selectedPosition = -1;
|
||||
|
||||
public interface OnTimeSlotClickListener {
|
||||
void onTimeSlotClick(String slot);
|
||||
}
|
||||
|
||||
public TimeSlotAdapter(List<String> timeSlots, OnTimeSlotClickListener listener) {
|
||||
this.timeSlots = timeSlots;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_time_slot, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
String slot = timeSlots.get(position);
|
||||
holder.txtTime.setText(slot);
|
||||
|
||||
boolean isSelected = selectedPosition == position;
|
||||
holder.card.setStrokeColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorPrimary) : holder.itemView.getContext().getColor(R.color.colorBorder));
|
||||
holder.card.setStrokeWidth(isSelected ? 4 : 2);
|
||||
holder.card.setCardBackgroundColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorChip) : Color.WHITE);
|
||||
holder.txtTime.setTextColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorPrimary) : holder.itemView.getContext().getColor(R.color.colorTextPrimary));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
int previousSelected = selectedPosition;
|
||||
int currentPos = holder.getAdapterPosition();
|
||||
|
||||
if (currentPos != RecyclerView.NO_POSITION) {
|
||||
selectedPosition = currentPos;
|
||||
if (previousSelected != -1) {
|
||||
notifyItemChanged(previousSelected);
|
||||
}
|
||||
notifyItemChanged(selectedPosition);
|
||||
listener.onTimeSlotClick(slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return timeSlots.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
MaterialCardView card;
|
||||
TextView txtTime;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
card = itemView.findViewById(R.id.cardTimeSlot);
|
||||
txtTime = itemView.findViewById(R.id.txtTimeSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,6 @@ public class InteractiveRatingBar extends LinearLayout {
|
||||
int sizePx = (int) (starSizeDp * context.getResources().getDisplayMetrics().density);
|
||||
int paddingPx = (int) (starPaddingDp * context.getResources().getDisplayMetrics().density);
|
||||
|
||||
stars.clear();
|
||||
removeAllViews();
|
||||
|
||||
for (int i = 0; i < numStars; i++) {
|
||||
ImageView star = new ImageView(context);
|
||||
LayoutParams params = new LayoutParams(sizePx, sizePx);
|
||||
@@ -94,12 +91,11 @@ public class InteractiveRatingBar extends LinearLayout {
|
||||
}
|
||||
|
||||
private void calculateRatingFromTouch(float x) {
|
||||
int count = stars.size();
|
||||
if (count == 0) return;
|
||||
if (stars.isEmpty()) return;
|
||||
|
||||
double newRating = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (int i = 0; i < numStars; i++) {
|
||||
ImageView star = stars.get(i);
|
||||
float starLeft = star.getLeft();
|
||||
float starRight = star.getRight();
|
||||
@@ -120,7 +116,7 @@ public class InteractiveRatingBar extends LinearLayout {
|
||||
}
|
||||
|
||||
// Clamp values
|
||||
newRating = Math.max(0.5, Math.min(newRating, (double) count));
|
||||
newRating = Math.max(0.5, Math.min(newRating, numStars));
|
||||
|
||||
if (newRating != currentRating) {
|
||||
currentRating = newRating;
|
||||
@@ -132,8 +128,7 @@ public class InteractiveRatingBar extends LinearLayout {
|
||||
}
|
||||
|
||||
private void updateStars(double rating, boolean animate) {
|
||||
int count = stars.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (int i = 0; i < numStars; i++) {
|
||||
ImageView star = stars.get(i);
|
||||
int previousResId = (Integer) (star.getTag() != null ? star.getTag() : 0);
|
||||
int newResId;
|
||||
|
||||
@@ -3,21 +3,17 @@ package com.example.pap_teste.models;
|
||||
public class FoodCategory {
|
||||
private String name;
|
||||
private int imageResId;
|
||||
private String imageUrl;
|
||||
|
||||
public FoodCategory() {}
|
||||
|
||||
public FoodCategory(String name, int imageResId) {
|
||||
this.name = name;
|
||||
this.imageResId = imageResId;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getImageResId() { return imageResId; }
|
||||
public void setImageResId(int imageResId) { this.imageResId = imageResId; }
|
||||
|
||||
public String getImageUrl() { return imageUrl; }
|
||||
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
|
||||
public int getImageResId() {
|
||||
return imageResId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.google.firebase.database.PropertyName;
|
||||
|
||||
public class OperatingHoursDay implements Serializable {
|
||||
@PropertyName("isOpen")
|
||||
private boolean openStatus;
|
||||
private String openTime;
|
||||
private String closeTime;
|
||||
|
||||
public OperatingHoursDay() {
|
||||
// Construtor vazio necessário para Firebase
|
||||
}
|
||||
|
||||
@PropertyName("isOpen")
|
||||
public boolean getOpenStatus() {
|
||||
return openStatus;
|
||||
}
|
||||
|
||||
@PropertyName("isOpen")
|
||||
public void setOpenStatus(boolean openStatus) {
|
||||
this.openStatus = openStatus;
|
||||
}
|
||||
|
||||
public String getOpenTime() {
|
||||
return openTime;
|
||||
}
|
||||
|
||||
public void setOpenTime(String openTime) {
|
||||
this.openTime = openTime;
|
||||
}
|
||||
|
||||
public String getCloseTime() {
|
||||
return closeTime;
|
||||
}
|
||||
|
||||
public void setCloseTime(String closeTime) {
|
||||
this.closeTime = closeTime;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import com.google.firebase.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
public class Reserva {
|
||||
private String id;
|
||||
private String clienteEmail;
|
||||
@@ -10,18 +7,16 @@ public class Reserva {
|
||||
private String restauranteEmail;
|
||||
private String data;
|
||||
private String hora;
|
||||
private com.google.firebase.Timestamp date;
|
||||
private int pessoas;
|
||||
private int tableNumber;
|
||||
private String estado; // "Pendente", "Confirmada", "Cancelada"
|
||||
private String specialRequests;
|
||||
private Timestamp createdAt;
|
||||
private String estado; // Pendente, Confirmada, Concluída, Cancelada, Recusada
|
||||
private String motivo;
|
||||
|
||||
public Reserva() {
|
||||
// Required for Firebase
|
||||
}
|
||||
|
||||
public Reserva(String id, String clienteEmail, String restauranteName, String restauranteEmail,
|
||||
String data, String hora, int pessoas, String estado) {
|
||||
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;
|
||||
@@ -30,56 +25,77 @@ public class Reserva {
|
||||
this.hora = hora;
|
||||
this.pessoas = pessoas;
|
||||
this.estado = estado;
|
||||
this.createdAt = new Timestamp(new Date());
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getClienteEmail() { return clienteEmail; }
|
||||
public void setClienteEmail(String clienteEmail) { this.clienteEmail = clienteEmail; }
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getRestauranteName() { return restauranteName; }
|
||||
public void setRestauranteName(String restauranteName) { this.restauranteName = restauranteName; }
|
||||
public String getClienteEmail() {
|
||||
return clienteEmail;
|
||||
}
|
||||
|
||||
public String getRestauranteEmail() { return restauranteEmail; }
|
||||
public void setRestauranteEmail(String restauranteEmail) { this.restauranteEmail = restauranteEmail; }
|
||||
public void setClienteEmail(String clienteEmail) {
|
||||
this.clienteEmail = clienteEmail;
|
||||
}
|
||||
|
||||
public String getData() { return data; }
|
||||
public void setData(String data) { this.data = data; }
|
||||
public String getRestauranteName() {
|
||||
return restauranteName;
|
||||
}
|
||||
|
||||
public String getHora() { return hora; }
|
||||
public void setHora(String hora) { this.hora = hora; }
|
||||
public void setRestauranteName(String restauranteName) {
|
||||
this.restauranteName = restauranteName;
|
||||
}
|
||||
|
||||
public com.google.firebase.Timestamp getDate() { return date; }
|
||||
public void setDate(com.google.firebase.Timestamp date) { this.date = date; }
|
||||
public String getRestauranteEmail() {
|
||||
return restauranteEmail;
|
||||
}
|
||||
|
||||
public int getPessoas() { return pessoas; }
|
||||
public void setPessoas(int pessoas) { this.pessoas = pessoas; }
|
||||
public void setRestauranteEmail(String restauranteEmail) {
|
||||
this.restauranteEmail = restauranteEmail;
|
||||
}
|
||||
|
||||
public int getTableNumber() { return tableNumber; }
|
||||
public void setTableNumber(int tableNumber) { this.tableNumber = tableNumber; }
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getEstado() { return estado; }
|
||||
public void setEstado(String estado) { this.estado = estado; }
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getSpecialRequests() { return specialRequests; }
|
||||
public void setSpecialRequests(String specialRequests) { this.specialRequests = specialRequests; }
|
||||
public String getHora() {
|
||||
return hora;
|
||||
}
|
||||
|
||||
public Timestamp getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
|
||||
public void setHora(String hora) {
|
||||
this.hora = hora;
|
||||
}
|
||||
|
||||
// Compatibility mappings for ReservationActivity
|
||||
public String getUserId() { return clienteEmail; }
|
||||
public void setUserId(String userId) { this.clienteEmail = userId; }
|
||||
public String getRestaurantId() { return restauranteEmail; }
|
||||
public void setRestaurantId(String restaurantId) { this.restauranteEmail = restaurantId; }
|
||||
public String getRestaurantName() { return restauranteName; }
|
||||
public void setRestaurantName(String restaurantName) { this.restauranteName = restaurantName; }
|
||||
public int getGuests() { return pessoas; }
|
||||
public void setGuests(int guests) { this.pessoas = guests; }
|
||||
public String getStatus() { return estado; }
|
||||
public void setStatus(String status) { this.estado = status; }
|
||||
public String getTimeSlot() { return hora; }
|
||||
public void setTimeSlot(String timeSlot) { this.hora = timeSlot; }
|
||||
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;
|
||||
}
|
||||
|
||||
public String getMotivo() {
|
||||
return motivo;
|
||||
}
|
||||
|
||||
public void setMotivo(String motivo) {
|
||||
this.motivo = motivo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +1,92 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Restaurant {
|
||||
private String id;
|
||||
public class Restaurant implements Serializable {
|
||||
private String name;
|
||||
private String category;
|
||||
private String email;
|
||||
private String description;
|
||||
private String imageURL;
|
||||
private double rating;
|
||||
private boolean available;
|
||||
private String logoUrl;
|
||||
private Double ratingAvg;
|
||||
private Integer ratingCount;
|
||||
private String address;
|
||||
private Map<String, String> openingHours;
|
||||
private List<String> availableSlots;
|
||||
private boolean favorite;
|
||||
private boolean fechadoWebsite;
|
||||
|
||||
public Restaurant() {}
|
||||
// No-argument constructor required for Firebase
|
||||
public Restaurant() {
|
||||
}
|
||||
|
||||
public Restaurant(String name, String category, String email, boolean favorite, String imageURL) {
|
||||
public Restaurant(String name, String category, String email, boolean available) {
|
||||
this.name = name;
|
||||
this.category = category;
|
||||
this.email = email;
|
||||
this.favorite = favorite;
|
||||
this.imageURL = imageURL;
|
||||
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 getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getCategory() { return category; }
|
||||
public void setCategory(String category) { this.category = 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; }
|
||||
public boolean isFechadoWebsite() { return fechadoWebsite; }
|
||||
public void setFechadoWebsite(boolean fechadoWebsite) { this.fechadoWebsite = fechadoWebsite; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getImageURL() { return imageURL; }
|
||||
public void setImageURL(String imageURL) { this.imageURL = imageURL; }
|
||||
|
||||
public double getRating() { return rating; }
|
||||
public void setRating(double rating) { this.rating = rating; }
|
||||
|
||||
public Double getRatingAvg() { return ratingAvg != null ? ratingAvg : rating; }
|
||||
public Double getRatingAvg() { return ratingAvg; }
|
||||
public void setRatingAvg(Double ratingAvg) { this.ratingAvg = ratingAvg; }
|
||||
|
||||
public Integer getRatingCount() { return ratingCount != null ? ratingCount : 0; }
|
||||
public Integer getRatingCount() { return ratingCount; }
|
||||
public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; }
|
||||
|
||||
public String getAddress() { return address; }
|
||||
public void setAddress(String address) { this.address = address; }
|
||||
|
||||
public Map<String, String> getOpeningHours() { return openingHours; }
|
||||
public void setOpeningHours(Map<String, String> openingHours) { this.openingHours = openingHours; }
|
||||
|
||||
public List<String> getAvailableSlots() { return availableSlots; }
|
||||
public void setAvailableSlots(List<String> availableSlots) { this.availableSlots = availableSlots; }
|
||||
|
||||
public boolean isFavorite() { return favorite; }
|
||||
public void setFavorite(boolean favorite) { this.favorite = favorite; }
|
||||
private java.util.Map<String, ScheduleDay> schedule;
|
||||
public java.util.Map<String, ScheduleDay> getSchedule() { return schedule; }
|
||||
public void setSchedule(java.util.Map<String, ScheduleDay> schedule) { this.schedule = schedule; }
|
||||
|
||||
// Compatibility mappings
|
||||
public String getCuisine() { return category; }
|
||||
public void setCuisine(String cuisine) { this.category = cuisine; }
|
||||
public String getLogoUrl() { return imageURL; }
|
||||
public void setLogoUrl(String logoUrl) { this.imageURL = logoUrl; }
|
||||
public ScheduleDay getScheduleForDay(int dayOfWeekCalendar) {
|
||||
if (schedule == null) return null;
|
||||
String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||
String[] dayKeysPt = {"domingo", "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", "sábado"};
|
||||
String[] dayKeysPtShort = {"domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado"};
|
||||
int index = dayOfWeekCalendar - 1;
|
||||
|
||||
ScheduleDay sd = schedule.get(dayKeys[index]);
|
||||
if (sd == null) sd = schedule.get(dayKeysPt[index]);
|
||||
if (sd == null) sd = schedule.get(dayKeysPtShort[index]);
|
||||
if (sd == null) sd = schedule.get(String.valueOf(index));
|
||||
|
||||
if (sd == null) {
|
||||
for (java.util.Map.Entry<String, ScheduleDay> entry : schedule.entrySet()) {
|
||||
String k = entry.getKey().toLowerCase(java.util.Locale.ROOT).replace("-", "").replace(" ", "");
|
||||
String expected1 = dayKeys[index].replace("-", "");
|
||||
String expected2 = dayKeysPt[index].replace("-", "");
|
||||
String expected3 = dayKeysPtShort[index].replace("-", "");
|
||||
|
||||
if (k.equals(expected1) || k.equals(expected2) || k.equals(expected3) || k.equals(String.valueOf(index))) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
|
||||
public boolean isClosedForDay(int dayOfWeekCalendar) {
|
||||
if (schedule == null || schedule.isEmpty()) return false; // If no schedule is set at all, default to open to avoid locking out old restaurants.
|
||||
ScheduleDay sd = getScheduleForDay(dayOfWeekCalendar);
|
||||
if (sd == null) {
|
||||
return true; // Se o dia não existe no mapa de um restaurante com horário, está fechado.
|
||||
}
|
||||
return sd.isClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ScheduleDay implements Serializable {
|
||||
private boolean closed;
|
||||
private Boolean isOpen;
|
||||
private String openTime;
|
||||
private String closeTime;
|
||||
|
||||
public ScheduleDay() {
|
||||
// No-argument constructor for Firebase
|
||||
}
|
||||
|
||||
public ScheduleDay(boolean closed, String openTime, String closeTime) {
|
||||
this.closed = closed;
|
||||
this.isOpen = !closed;
|
||||
this.openTime = openTime;
|
||||
this.closeTime = closeTime;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
if (isOpen != null) {
|
||||
return !isOpen;
|
||||
}
|
||||
return closed;
|
||||
}
|
||||
|
||||
public void setClosed(boolean closed) {
|
||||
this.closed = closed;
|
||||
if (this.isOpen == null) {
|
||||
this.isOpen = !closed;
|
||||
}
|
||||
}
|
||||
|
||||
// Handles potential "isClosed" boolean in Firebase
|
||||
public void setIsClosed(Boolean isClosed) {
|
||||
if (isClosed != null) {
|
||||
this.closed = isClosed;
|
||||
this.isOpen = !isClosed;
|
||||
}
|
||||
}
|
||||
|
||||
// Handles potential "fechado" boolean from Portuguese web dashboards
|
||||
public void setFechado(Boolean fechado) {
|
||||
if (fechado != null) {
|
||||
this.closed = fechado;
|
||||
this.isOpen = !fechado;
|
||||
}
|
||||
}
|
||||
|
||||
@com.google.firebase.database.PropertyName("isOpen")
|
||||
public Boolean getIsOpen() {
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
@com.google.firebase.database.PropertyName("isOpen")
|
||||
public void setIsOpen(Boolean isOpen) {
|
||||
this.isOpen = isOpen;
|
||||
}
|
||||
|
||||
public String getOpenTime() {
|
||||
return openTime;
|
||||
}
|
||||
|
||||
public void setOpenTime(String openTime) {
|
||||
this.openTime = openTime;
|
||||
}
|
||||
|
||||
public String getCloseTime() {
|
||||
return closeTime;
|
||||
}
|
||||
|
||||
public void setCloseTime(String closeTime) {
|
||||
this.closeTime = closeTime;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import com.google.firebase.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
public class User {
|
||||
private String id;
|
||||
private String displayName;
|
||||
private String email;
|
||||
private String photoURL;
|
||||
private String role; // "CLIENTE" or "RESTAURANTE"
|
||||
private Timestamp createdAt;
|
||||
|
||||
public User() {}
|
||||
|
||||
public User(String id, String displayName, String email, String role) {
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this.email = email;
|
||||
this.role = role;
|
||||
this.createdAt = new Timestamp(new Date());
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public String getDisplayName() { return displayName; }
|
||||
public void setDisplayName(String displayName) { this.displayName = displayName; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
public String getPhotoURL() { return photoURL; }
|
||||
public void setPhotoURL(String photoURL) { this.photoURL = photoURL; }
|
||||
public String getRole() { return role; }
|
||||
public void setRole(String role) { this.role = role; }
|
||||
public Timestamp getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="#00000000"
|
||||
android:startColor="#CC000000" />
|
||||
</shape>
|
||||
13
app/src/main/res/drawable/ic_clock_gold.xml
Normal file
13
app/src/main/res/drawable/ic_clock_gold.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#D4AF37">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||
</vector>
|
||||
164
app/src/main/res/layout/activity_add_staff.xml
Normal file
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>
|
||||
@@ -8,7 +8,7 @@
|
||||
android:background="@color/colorBackground"
|
||||
tools:context=".ClientDashboardActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
<ScrollView
|
||||
android:id="@+id/clientScroll"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
@@ -36,37 +36,22 @@
|
||||
android:id="@+id/txtClientGreeting"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="Olá, convidado!"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/imgNotification"
|
||||
android:layout_marginEnd="16dp" />
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Encontre a sua próxima mesa."
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/txtClientGreeting"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/imgNotification" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/imgNotification"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/bg_circle_white"
|
||||
android:src="@android:drawable/ic_popup_reminder"
|
||||
app:tint="@color/colorTextPrimary"
|
||||
android:layout_marginEnd="12dp"
|
||||
app:layout_constraintTop_toTopOf="@id/cardProfile"
|
||||
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
|
||||
app:layout_constraintEnd_toStartOf="@id/cardProfile" />
|
||||
app:layout_constraintTop_toBottomOf="@id/txtClientGreeting" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/cardProfile"
|
||||
@@ -127,24 +112,6 @@
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Category Pills -->
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:scrollbars="none"
|
||||
android:clipToPadding="false"
|
||||
android:paddingHorizontal="20dp">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/chipGroupCategories"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleLine="true"
|
||||
app:singleSelection="true">
|
||||
<!-- Chips will be added programmatically -->
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<!-- Loading Spinner -->
|
||||
<ProgressBar
|
||||
@@ -156,7 +123,7 @@
|
||||
android:visibility="visible"
|
||||
android:indeterminateTint="@color/colorPrimary" />
|
||||
|
||||
<!-- Featured / Destaques -->
|
||||
<!-- Featured Carousel -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutFeatured"
|
||||
android:layout_width="match_parent"
|
||||
@@ -169,7 +136,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Destaques"
|
||||
android:text="Restaurantes Populares"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
@@ -188,201 +155,17 @@
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Melhor Avaliados -->
|
||||
<!-- Dynamic Categories Container -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutTopRated"
|
||||
android:id="@+id/categoriesContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Melhor Avaliados"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvTopRated"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Pizzas -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPizzas"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Pizzas"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvPizzas"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Sushi -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutSushi"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Sushi"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvSushi"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Carnes -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutCarnes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Carnes"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvCarnes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Massas -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutMassas"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Massas"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvMassas"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Sobremesas -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutSobremesas"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="Sobremesas"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvSobremesas"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingBottom="16dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Bottom Navigation Bar -->
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottomNavigation"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottomNavigation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
app:elevation="8dp"
|
||||
app:itemIconTint="@color/colorPrimary"
|
||||
app:itemTextColor="@color/colorPrimary"
|
||||
app:labelVisibilityMode="labeled"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:menu="@menu/bottom_nav_menu" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
192
app/src/main/res/layout/activity_definicoes_admin.xml
Normal file
192
app/src/main/res/layout/activity_definicoes_admin.xml
Normal file
@@ -0,0 +1,192 @@
|
||||
<?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" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="Horário de Funcionamento"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="16sp"
|
||||
android:fontFamily="sans-serif-medium" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/containerSchedule"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<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
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>
|
||||
363
app/src/main/res/layout/activity_establishment_dashboard.xml
Normal file
363
app/src/main/res/layout/activity_establishment_dashboard.xml
Normal file
@@ -0,0 +1,363 @@
|
||||
<?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="vertical">
|
||||
|
||||
<!-- MESAS OCUPADAS CARD -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="#1F1D1A"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lblMesasOcupadas"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MESAS OCUPADAS"
|
||||
android:textColor="#888175"
|
||||
android:textSize="14sp"
|
||||
android:letterSpacing="0.05"
|
||||
android:fontFamily="serif"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_clock_gold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/lblMesasOcupadas" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtMesasLivresDash"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="0 / 0"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="38sp"
|
||||
android:fontFamily="serif-monospace"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/lblMesasOcupadas" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtMesasLivresSub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="0 livres"
|
||||
android:textColor="#D4AF37"
|
||||
android:textSize="16sp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/txtMesasLivresDash" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- RESERVAS CARD -->
|
||||
<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>
|
||||
|
||||
<!-- ESPERA CARD -->
|
||||
<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>
|
||||
</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>
|
||||
172
app/src/main/res/layout/activity_gerir_mesas.xml
Normal file
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
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
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>
|
||||
@@ -2,174 +2,229 @@
|
||||
<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/colorPrimary"
|
||||
android:id="@+id/main"
|
||||
android:background="@color/colorBackground"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgBg"
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:alpha="0.3"
|
||||
android:src="@drawable/na_mesa" />
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="80dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:src="@drawable/na_mesa" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="NaMesa"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="32sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Premium Dining Reservations"
|
||||
android:textColor="@color/white"
|
||||
android:alpha="0.7"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/cardContainer"
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
app:cardBackgroundColor="@color/white"
|
||||
app:cardElevation="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/toggleGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
app:singleSelection="true"
|
||||
app:checkedButton="@+id/btnEntrar">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEntrar"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Entrar" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnCriarConta"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Registar" />
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layoutName"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:hint="Nome Completo"
|
||||
android:visibility="gone"
|
||||
app:startIconDrawable="@android:drawable/ic_menu_edit">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPersonName" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:hint="Email"
|
||||
app:startIconDrawable="@android:drawable/ic_dialog_email">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputEmail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:hint="Palavra-passe"
|
||||
app:endIconMode="password_toggle"
|
||||
app:startIconDrawable="@android:drawable/ic_lock_idle_lock">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/inputPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<ImageView
|
||||
android:id="@+id/logoNaMesa"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="156dp"
|
||||
android:layout_marginTop="124dp"
|
||||
android:src="@drawable/na_mesa"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.508"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtForgotPassword"
|
||||
android:id="@+id/txtTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="Esqueceu-se da senha?"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
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" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPrimaryAction"
|
||||
style="@style/Widget.NaMesa.Button"
|
||||
<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="60dp"
|
||||
android:text="Entrar"
|
||||
android:textSize="16sp" />
|
||||
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>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="387dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarLogin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_visibility_off" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtForgotPassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:text="Esqueceu-se da palavra-passe?"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="14sp"
|
||||
android:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnFinalCriarConta"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="Entrar"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
app:cornerRadius="14dp" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -122,6 +122,19 @@
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtRestauranteFechado"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Restaurante fechado temporariamente. Não é possível efetuar reservas."
|
||||
android:textColor="@android:color/holo_red_dark"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
android:padding="8dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -161,24 +174,6 @@
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
app:strokeColor="@color/colorDivider" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="Escolha a mesa"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvTables"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
tools:listitem="@layout/item_table" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -198,28 +193,6 @@
|
||||
android:hint="Ex: 2"
|
||||
android:textColorHint="@color/colorTextHint"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="Notas adicionais (opcional)"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etNotes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_input_modern"
|
||||
android:inputType="textMultiLine"
|
||||
android:minHeight="100dp"
|
||||
android:gravity="top"
|
||||
android:padding="16dp"
|
||||
android:hint="Alguma preferência?"
|
||||
android:textColorHint="@color/colorTextHint"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:src="@drawable/na_mesa"
|
||||
app:layout_constraintBottom_toTopOf="@+id/tagline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="A sua mesa, à sua espera."
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/logo" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnGetStarted"
|
||||
style="@style/Widget.NaMesa.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:text="Começar"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,203 +0,0 @@
|
||||
<?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:background="@color/white">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/white"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@android:drawable/ic_menu_revert"
|
||||
app:title="Reservar Mesa"
|
||||
app:titleTextColor="@color/colorTextPrimary" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:fillViewport="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/confirmContainer"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<!-- Number of People -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Número de Pessoas"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnDecreaseGuests"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:src="@android:drawable/ic_menu_delete"
|
||||
app:tint="@color/colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtGuestCount"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="2"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnIncreaseGuests"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:src="@android:drawable/ic_menu_add"
|
||||
app:tint="@color/colorPrimary" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Date Selection -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Data da Reserva"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/cardDatePicker"
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_my_calendar"
|
||||
app:tint="@color/colorTextSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtSelectedDate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_weight="1"
|
||||
android:text="Selecionar Data"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Time Slots -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Horários Disponíveis"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvTimeSlots"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
tools:listitem="@layout/item_time_slot" />
|
||||
|
||||
<!-- Special Requests -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Pedidos Especiais (Opcional)"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSpecialRequests"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@null"
|
||||
android:gravity="top"
|
||||
android:hint="Ex: Cadeira para bebé, alergias..."
|
||||
android:padding="16dp"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textColorHint="@color/colorTextHint"
|
||||
android:textSize="15sp" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/confirmContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="@color/white"
|
||||
app:cardCornerRadius="0dp"
|
||||
app:cardElevation="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnConfirmReservation"
|
||||
style="@style/Widget.NaMesa.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_margin="20dp"
|
||||
android:text="Confirmar Reserva"
|
||||
android:textSize="18sp" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,163 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:background="@color/white">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:background="@color/white"
|
||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:id="@+id/collapsingToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:contentScrim="@color/colorPrimary"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||
app:titleEnabled="false">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/restaurantCover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/na_mesa"
|
||||
app:layout_collapseMode="parallax" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/gradient_overlay" />
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_collapseMode="pin"
|
||||
app:navigationIcon="@android:drawable/ic_menu_revert" />
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Nome do Restaurante"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="28sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailCuisine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Italiano"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:text="•"
|
||||
android:textColor="@color/colorTextSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailRating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="4.8 ★"
|
||||
android:textColor="@color/colorWarning"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Sobre"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="Descrição do restaurante aparecerá aqui com todos os detalhes premium."
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Localização"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Rua Principal, 123, Lisboa"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<!-- Spacer for bottom button -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:cardBackgroundColor="@color/white"
|
||||
app:cardCornerRadius="0dp"
|
||||
app:cardElevation="16dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnReserveNow"
|
||||
style="@style/Widget.NaMesa.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_margin="20dp"
|
||||
android:text="Reservar Mesa"
|
||||
android:textSize="18sp" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,47 +0,0 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/colorPrimary">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/splash_logo"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:src="@drawable/na_mesa"
|
||||
app:layout_constraintBottom_toTopOf="@+id/splash_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/splash_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="NaMesa"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="40sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/splash_subtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/splash_logo" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/splash_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:alpha="0.8"
|
||||
android:text="Sempre um lugar para si"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/splash_title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,174 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView 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:background="@color/white"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="80dp">
|
||||
|
||||
<!-- Header -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingTop="32dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/greeting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Olá, Explorador"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="32sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Onde gostaria de jantar hoje?"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@android:drawable/ic_menu_search"
|
||||
app:tint="@color/colorTextSecondary" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@null"
|
||||
android:hint="Procurar restaurantes, cozinhas..."
|
||||
android:inputType="text"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textColorHint="@color/colorTextHint"
|
||||
android:textSize="15sp" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- Categories -->
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/categoryChips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleLine="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tudo"
|
||||
app:chipBackgroundColor="@color/colorChip"
|
||||
app:chipStrokeWidth="0dp" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Italiano"
|
||||
app:chipBackgroundColor="@color/colorChip"
|
||||
app:chipStrokeWidth="0dp" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sushi"
|
||||
app:chipBackgroundColor="@color/colorChip"
|
||||
app:chipStrokeWidth="0dp" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hambúrgueres"
|
||||
app:chipBackgroundColor="@color/colorChip"
|
||||
app:chipStrokeWidth="0dp" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
</HorizontalScrollView>
|
||||
|
||||
<!-- Featured Section -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Destaques"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="22sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvFeatured"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="24dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
|
||||
<!-- Near You Section -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Perto de si"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="22sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvNearYou"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingHorizontal="24dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_card" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -1,71 +0,0 @@
|
||||
<?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:background="@color/white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Minhas Reservas"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="28sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvReservations"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="80dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/txtTitle"
|
||||
tools:listitem="@layout/item_reserva_cliente" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/emptyState"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:alpha="0.5"
|
||||
android:src="@drawable/na_mesa" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Ainda não tem reservas."
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,121 +0,0 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<View
|
||||
android:id="@+id/headerBg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:background="@color/colorPrimary"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/cardProfile"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="140dp"
|
||||
app:cardCornerRadius="60dp"
|
||||
app:cardElevation="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:strokeColor="@color/white"
|
||||
app:strokeWidth="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgProfile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/na_mesa" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtProfileName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Nome do Utilizador"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/cardProfile" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtProfileEmail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="email@exemplo.com"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/txtProfileName" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="24dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/txtProfileEmail">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEditProfile"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="Editar Perfil"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="16sp"
|
||||
app:icon="@android:drawable/ic_menu_edit"
|
||||
app:iconPadding="16dp"
|
||||
app:iconTint="@color/colorPrimary" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/colorBorder" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnNotifications"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="Notificações"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="16sp"
|
||||
app:icon="@android:drawable/ic_popup_reminder"
|
||||
app:iconPadding="16dp"
|
||||
app:iconTint="@color/colorPrimary" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/colorBorder" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnLogout"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="Sair da Conta"
|
||||
android:textColor="@color/colorError"
|
||||
android:textSize="16sp"
|
||||
app:icon="@android:drawable/ic_lock_power_off"
|
||||
app:iconPadding="16dp"
|
||||
app:iconTint="@color/colorError" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
32
app/src/main/res/layout/item_category_row.xml
Normal file
32
app/src/main/res/layout/item_category_row.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtCategoryTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
tools:text="Sushi" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvCategoryRestaurants"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_restaurant_featured" />
|
||||
</LinearLayout>
|
||||
@@ -1,12 +1,13 @@
|
||||
<?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"
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
app:cardCornerRadius="20dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardBackgroundColor="@color/colorSurface"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
<?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:strokeWidth="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtRestauranteNome"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Nome do Restaurante"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipStatus"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="PENDENTE"
|
||||
app:chipBackgroundColor="@color/colorChipPending"
|
||||
android:textColor="@color/colorChipPendingText" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtDataHora"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="15/05/2026 às 20:00"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtMesaPessoas"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Mesa 5 • 4 pessoas"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
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/btnCancelar"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cancelar"
|
||||
android:textColor="@color/colorError" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEditar"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ver Detalhes"
|
||||
android:textColor="@color/colorPrimary" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -1,67 +0,0 @@
|
||||
<?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"
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardElevation="2dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="12dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/restaurantImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/na_mesa" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/restaurantName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Nome do Restaurante"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/restaurantCuisine"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Cozinha • 4.9 ★"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Ver detalhes"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -1,11 +1,11 @@
|
||||
<?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"
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
app:cardCornerRadius="24dp"
|
||||
app:cardElevation="4dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
@@ -14,40 +14,112 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/restaurantImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/imgFeaturedCover"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/na_mesa" />
|
||||
android:src="@mipmap/ic_launcher"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/gradient_overlay" />
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:background="@drawable/gradient_shadow_up"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtFeaturedName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:text="Restaurante"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
app:layout_constraintBottom_toTopOf="@id/layoutRating"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnFavorite"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/bg_circle_white"
|
||||
android:padding="8dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@android:drawable/btn_star_big_off"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:tint="@color/colorTextPrimary" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/layoutRating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@android:drawable/star_on"
|
||||
app:tint="@color/colorWarning" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/restaurantName"
|
||||
android:id="@+id/txtFeaturedRating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="Nome do Restaurante"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="4.8"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp" />
|
||||
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/restaurantCuisine"
|
||||
android:id="@+id/txtFeaturedCategory"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cozinha • 4.9 ★"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginStart="6dp"
|
||||
android:text="• Sushi"
|
||||
android:textColor="#E0E0E0"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardBackgroundColor="#CCFFFFFF"
|
||||
app:strokeWidth="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:text="Destaque"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
52
app/src/main/res/layout/item_schedule_day.xml
Normal file
52
app/src/main/res/layout/item_schedule_day.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cbClosed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Folga"
|
||||
android:textColor="@color/colorTextSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDayName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Segunda-feira"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="14sp"
|
||||
android:fontFamily="sans-serif-medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOpenTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="09:00"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/input_bg"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:textColor="@color/colorTextSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCloseTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="22:00"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/input_bg"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,48 +0,0 @@
|
||||
<?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:id="@+id/cardTable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="2dp"
|
||||
app:strokeWidth="2dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:cardBackgroundColor="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgTableStatus"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_agenda"
|
||||
app:tint="@color/colorTextSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTableNumber"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Mesa 1"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTableStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Livre"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?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:id="@+id/cardTimeSlot"
|
||||
style="@style/Widget.NaMesa.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_margin="4dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:strokeWidth="1dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTimeSlot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="19:30"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkable="true"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
app:chipBackgroundColor="@color/colorChip"
|
||||
app:chipStrokeWidth="0dp"
|
||||
app:rippleColor="@color/colorPrimary" />
|
||||
@@ -1,33 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Brand Colors - Midnight Blue Premium Palette -->
|
||||
<color name="colorPrimary">#1A237E</color>
|
||||
<color name="colorPrimaryVariant">#000051</color>
|
||||
<color name="colorSecondary">#3F51B5</color>
|
||||
<!-- Brand Colors - Warm Premium Palette -->
|
||||
<color name="colorPrimary">#FF5A5F</color> <!-- Airbnb/UberEats Coral -->
|
||||
<color name="colorPrimaryVariant">#FF3B30</color>
|
||||
<color name="colorSecondary">#FF5A5F</color>
|
||||
|
||||
<!-- Neutral Colors -->
|
||||
<color name="black">#121212</color>
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="colorBackground">#FFFFFF</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="colorBackground">#F7F7F9</color> <!-- Soft grey for modern background -->
|
||||
<color name="colorSurface">#FFFFFF</color>
|
||||
<color name="colorTextPrimary">#121212</color>
|
||||
<color name="colorTextSecondary">#666666</color>
|
||||
<color name="colorTextHint">#A0A0A0</color>
|
||||
<color name="colorBorder">#EEEEEE</color>
|
||||
<color name="colorTextPrimary">#1A1A1A</color> <!-- Dark but not pure black -->
|
||||
<color name="colorTextSecondary">#757575</color>
|
||||
<color name="colorTextHint">#BDBDBD</color>
|
||||
|
||||
<!-- Semantic Colors -->
|
||||
<color name="colorSuccess">#00897B</color>
|
||||
<color name="colorError">#D32F2F</color>
|
||||
<color name="colorWarning">#FFA000</color>
|
||||
<color name="colorDivider">#F5F5F5</color>
|
||||
<color name="colorChip">#F8F9FA</color>
|
||||
<color name="colorAccent">#1A237E</color>
|
||||
|
||||
<!-- Status Colors (Restored for compatibility and new design) -->
|
||||
<color name="colorChipPending">#FFF3E0</color>
|
||||
<color name="colorSuccess">#34C759</color> <!-- iOS green -->
|
||||
<color name="colorError">#FF3B30</color>
|
||||
<color name="colorWarning">#FFCC00</color>
|
||||
<color name="colorDivider">#E0E0E0</color>
|
||||
<color name="colorChipPending">#FFE0B2</color>
|
||||
<color name="colorChipPendingText">#E65100</color>
|
||||
<color name="colorChipConfirmed">#E8F5E9</color>
|
||||
<color name="colorChipConfirmedText">#2E7D32</color>
|
||||
<color name="colorChipCancelled">#FFEBEE</color>
|
||||
<color name="colorChipCancelledText">#C62828</color>
|
||||
<color name="colorChipConfirmed">#C8E6C9</color>
|
||||
<color name="colorChipConfirmedText">#1B5E20</color>
|
||||
<color name="colorChipCancelled">#FFCDD2</color>
|
||||
<color name="colorChipCancelledText">#B71C1C</color>
|
||||
|
||||
<!-- Legacy compatibility -->
|
||||
<color name="my_light_primary">#BE1F13</color>
|
||||
</resources>
|
||||
@@ -1,45 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.Pap_teste" parent="Theme.Material3.Light.NoActionBar">
|
||||
<style name="Base.Theme.Pap_teste" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Core Colors -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||
<item name="colorSecondary">@color/colorSecondary</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
||||
<!-- Background and Surface Colors -->
|
||||
<item name="android:colorBackground">@color/colorBackground</item>
|
||||
<item name="colorSurface">@color/colorSurface</item>
|
||||
<item name="colorError">@color/colorError</item>
|
||||
|
||||
<!-- Status Bar -->
|
||||
<item name="android:statusBarColor">@color/white</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<!-- Typography base generic configuration -->
|
||||
<item name="android:fontFamily">sans-serif</item>
|
||||
|
||||
<!-- Typography -->
|
||||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
<!-- Modern Shape Appearance (Rounded Corners) -->
|
||||
<item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.App.SmallComponent</item>
|
||||
<item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.App.MediumComponent</item>
|
||||
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.App.LargeComponent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Pap_teste" parent="Base.Theme.Pap_teste" />
|
||||
|
||||
<!-- Premium Button Style -->
|
||||
<style name="Widget.NaMesa.Button" parent="Widget.Material3.Button">
|
||||
<item name="shapeAppearanceOverlay">@style/ShapeAppearance.NaMesa.Rounded</item>
|
||||
<item name="android:insetTop">0dp</item>
|
||||
<item name="android:insetBottom">0dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:letterSpacing">0.02</item>
|
||||
<!-- Shape Styles -->
|
||||
<style name="ShapeAppearance.App.SmallComponent" parent="ShapeAppearance.Material3.SmallComponent">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">12dp</item>
|
||||
</style>
|
||||
|
||||
<!-- Premium Card Style -->
|
||||
<style name="Widget.NaMesa.Card" parent="Widget.Material3.CardView.Elevated">
|
||||
<item name="cardCornerRadius">16dp</item>
|
||||
<item name="cardElevation">2dp</item>
|
||||
<item name="cardBackgroundColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="ShapeAppearance.NaMesa.Rounded" parent="">
|
||||
<style name="ShapeAppearance.App.MediumComponent" parent="ShapeAppearance.Material3.MediumComponent">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="ShapeAppearance.App.LargeComponent" parent="ShapeAppearance.Material3.LargeComponent">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">24dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
agp = "9.1.0"
|
||||
agp = "9.1.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
@@ -13,7 +13,6 @@ googleServices = "4.4.2"
|
||||
firebaseDatabase = "22.0.1"
|
||||
glide = "4.16.0"
|
||||
firebaseStorage = "21.0.1"
|
||||
room = "2.6.1"
|
||||
|
||||
[libraries]
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
@@ -28,9 +27,6 @@ firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.r
|
||||
firebase-database = { group = "com.google.firebase", name = "firebase-database", version.ref = "firebaseDatabase" }
|
||||
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
|
||||
firebase-storage = { group = "com.google.firebase", name = "firebase-storage", version.ref = "firebaseStorage" }
|
||||
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Reference in New Issue
Block a user