Commit 779cb00d authored by Syazmin's avatar Syazmin

Appointment Details and Listing Done

parent ecd23c3b
...@@ -19,6 +19,7 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt ...@@ -19,6 +19,7 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt
private List<AppointmentEntity> appointments = new ArrayList<>(); private List<AppointmentEntity> appointments = new ArrayList<>();
private final OnAppointmentClickListener listener; private final OnAppointmentClickListener listener;
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("EEE, d MMM yyyy", Locale.getDefault()); private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("EEE, d MMM yyyy", Locale.getDefault());
private boolean isCalendarView = false; // Flag to determine which layout to use
public interface OnAppointmentClickListener { public interface OnAppointmentClickListener {
void onAppointmentClick(AppointmentEntity appointment); void onAppointmentClick(AppointmentEntity appointment);
...@@ -28,12 +29,18 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt ...@@ -28,12 +29,18 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt
this.listener = listener; this.listener = listener;
} }
public void setCalendarView(boolean isCalendarView) {
this.isCalendarView = isCalendarView;
notifyDataSetChanged();
}
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
int layoutId = isCalendarView ? R.layout.item_appointment_calendar : R.layout.item_appointment_new;
View view = LayoutInflater.from(parent.getContext()) View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_appointment_new, parent, false); .inflate(layoutId, parent, false);
return new ViewHolder(view); return new ViewHolder(view, isCalendarView);
} }
@Override @Override
...@@ -41,12 +48,36 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt ...@@ -41,12 +48,36 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt
AppointmentEntity appointment = appointments.get(position); AppointmentEntity appointment = appointments.get(position);
// Show date header only if it's a different date from previous item // Show date header only if it's a different date from previous item
if (position == 0 || !isSameDate(appointments.get(position - 1), appointment)) { boolean showDateHeader = position == 0 || !isSameDate(appointments.get(position - 1), appointment);
holder.dateHeader.setVisibility(View.VISIBLE); LocalDate date = LocalDate.ofEpochDay(appointment.getSelectedDate());
LocalDate date = LocalDate.ofEpochDay(appointment.getSelectedDate());
holder.dateHeader.setText(date.format(dateFormatter)); if (isCalendarView) {
// Calendar view: date on LEFT
if (holder.dateHeaderContainer != null && holder.dateSpacer != null) {
if (showDateHeader) {
holder.dateHeaderContainer.setVisibility(View.VISIBLE);
holder.dateSpacer.setVisibility(View.GONE);
if (holder.dateDay != null) {
holder.dateDay.setText(String.valueOf(date.getDayOfMonth()));
}
if (holder.dateWeekday != null) {
holder.dateWeekday.setText(date.format(DateTimeFormatter.ofPattern("EEE", Locale.getDefault())));
}
} else {
holder.dateHeaderContainer.setVisibility(View.GONE);
holder.dateSpacer.setVisibility(View.VISIBLE);
}
}
} else { } else {
holder.dateHeader.setVisibility(View.GONE); // List view: date header at TOP
if (holder.dateHeader != null) {
if (showDateHeader) {
holder.dateHeader.setVisibility(View.VISIBLE);
holder.dateHeader.setText(date.format(dateFormatter));
} else {
holder.dateHeader.setVisibility(View.GONE);
}
}
} }
// Set time // Set time
...@@ -67,17 +98,22 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt ...@@ -67,17 +98,22 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt
String type = appointment.getAppointmentType(); String type = appointment.getAppointmentType();
holder.typeBadge.setText(type); holder.typeBadge.setText(type);
// Set badge background based on type // Set badge background and color bar based on type
if (type.equals("Handover")) { if (type.equals("Handover")) {
holder.typeBadge.setBackgroundResource(R.drawable.badge_handover); holder.typeBadge.setBackgroundResource(R.drawable.badge_handover);
holder.colorBar.setBackgroundColor(0xFF0E7392); // Teal
} else if (type.equals("Rectification")) { } else if (type.equals("Rectification")) {
holder.typeBadge.setBackgroundResource(R.drawable.badge_rectification); holder.typeBadge.setBackgroundResource(R.drawable.badge_rectification);
holder.colorBar.setBackgroundColor(0xFFFF6B35); // Orange
} else if (type.equals("Joint Inspection")) { } else if (type.equals("Joint Inspection")) {
holder.typeBadge.setBackgroundResource(R.drawable.badge_joint_inspection); holder.typeBadge.setBackgroundResource(R.drawable.badge_joint_inspection);
holder.colorBar.setBackgroundColor(0xFFD32F2F); // Red
} else if (type.equals("Viewing")) { } else if (type.equals("Viewing")) {
holder.typeBadge.setBackgroundResource(R.drawable.badge_confirmed); holder.typeBadge.setBackgroundResource(R.drawable.badge_confirmed);
holder.colorBar.setBackgroundColor(0xFF4CAF50); // Green
} else { } else {
holder.typeBadge.setBackgroundResource(R.drawable.badge_handover); holder.typeBadge.setBackgroundResource(R.drawable.badge_handover);
holder.colorBar.setBackgroundColor(0xFF0E7392); // Teal
} }
// Click listener // Click listener
...@@ -99,19 +135,42 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt ...@@ -99,19 +135,42 @@ public class AppointmentAdapterNew extends RecyclerView.Adapter<AppointmentAdapt
} }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
// Calendar view fields (date on LEFT)
View dateHeaderContainer;
TextView dateDay;
TextView dateWeekday;
View dateSpacer;
// List view fields (date at TOP)
TextView dateHeader; TextView dateHeader;
// Common fields
TextView time; TextView time;
TextView unit; TextView unit;
TextView typeBadge; TextView typeBadge;
TextView location; TextView location;
View colorBar;
ViewHolder(View itemView) { ViewHolder(View itemView, boolean isCalendarView) {
super(itemView); super(itemView);
dateHeader = itemView.findViewById(R.id.date_header);
if (isCalendarView) {
// Calendar view layout
dateHeaderContainer = itemView.findViewById(R.id.date_header_container);
dateDay = itemView.findViewById(R.id.date_day);
dateWeekday = itemView.findViewById(R.id.date_weekday);
dateSpacer = itemView.findViewById(R.id.date_spacer);
} else {
// List view layout
dateHeader = itemView.findViewById(R.id.date_header);
}
// Common fields
time = itemView.findViewById(R.id.appointment_time); time = itemView.findViewById(R.id.appointment_time);
unit = itemView.findViewById(R.id.appointment_unit); unit = itemView.findViewById(R.id.appointment_unit);
typeBadge = itemView.findViewById(R.id.appointment_type_badge); typeBadge = itemView.findViewById(R.id.appointment_type_badge);
location = itemView.findViewById(R.id.appointment_location); location = itemView.findViewById(R.id.appointment_location);
colorBar = itemView.findViewById(R.id.appointment_color_bar);
} }
} }
} }
...@@ -13,7 +13,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog; ...@@ -13,7 +13,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog;
public class AppointmentListActivity extends AppCompatActivity { public class AppointmentListActivity extends AppCompatActivity {
private ImageButton backButton; private ImageButton backButton;
private ImageButton filterButton; private ImageButton toggleViewButton;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
...@@ -21,7 +21,7 @@ public class AppointmentListActivity extends AppCompatActivity { ...@@ -21,7 +21,7 @@ public class AppointmentListActivity extends AppCompatActivity {
setContentView(R.layout.activity_appointment_list); setContentView(R.layout.activity_appointment_list);
backButton = findViewById(R.id.back_button); backButton = findViewById(R.id.back_button);
filterButton = findViewById(R.id.filter_button); toggleViewButton = findViewById(R.id.toggle_view_button);
// Load the appointment list fragment // Load the appointment list fragment
if (savedInstanceState == null) { if (savedInstanceState == null) {
...@@ -41,8 +41,14 @@ public class AppointmentListActivity extends AppCompatActivity { ...@@ -41,8 +41,14 @@ public class AppointmentListActivity extends AppCompatActivity {
} }
}); });
// Filter button - Can be used for additional sorting options if needed // Toggle view button - switch between calendar and list view
filterButton.setOnClickListener(v -> showFilterDialog()); toggleViewButton.setOnClickListener(v -> {
AppointmentListFragmentNew fragment = (AppointmentListFragmentNew) getSupportFragmentManager()
.findFragmentById(R.id.fragment_container_list);
if (fragment != null) {
fragment.toggleView();
}
});
} }
private void showFilterDialog() { private void showFilterDialog() {
......
...@@ -9,22 +9,27 @@ import android.view.View; ...@@ -9,22 +9,27 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.chip.Chip;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textfield.TextInputEditText;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
...@@ -36,7 +41,13 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -36,7 +41,13 @@ public class AppointmentListFragmentNew extends Fragment {
private SwipeRefreshLayout swipeRefresh; private SwipeRefreshLayout swipeRefresh;
private AutoCompleteTextView typeFilterDropdown; private AutoCompleteTextView typeFilterDropdown;
private AutoCompleteTextView statusFilterDropdown; private AutoCompleteTextView statusFilterDropdown;
private TextInputEditText searchInput; private LinearLayout calendarDaysContainer;
private Chip chipConfirmed, chipAttended, chipHandedOver;
private View calendarViewContainer;
private View listViewContainer;
private View statusBadgesContainer;
private View searchLayout;
private com.google.android.material.textfield.TextInputEditText searchInput;
private AppointmentDatabase db; private AppointmentDatabase db;
private final ExecutorService executorService = Executors.newSingleThreadExecutor(); private final ExecutorService executorService = Executors.newSingleThreadExecutor();
...@@ -44,6 +55,9 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -44,6 +55,9 @@ public class AppointmentListFragmentNew extends Fragment {
private List<AppointmentEntity> allAppointments = new ArrayList<>(); private List<AppointmentEntity> allAppointments = new ArrayList<>();
private String currentTypeFilter = "All Type"; private String currentTypeFilter = "All Type";
private String currentStatusFilter = "Upcoming"; private String currentStatusFilter = "Upcoming";
private LocalDate selectedDate = null;
private boolean isCalendarViewMode = true; // Start with calendar visible
private String searchText = ""; // Store search text
private static final String STATE_TYPE_FILTER = "type_filter"; private static final String STATE_TYPE_FILTER = "type_filter";
private static final String STATE_STATUS_FILTER = "status_filter"; private static final String STATE_STATUS_FILTER = "status_filter";
...@@ -71,9 +85,14 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -71,9 +85,14 @@ public class AppointmentListFragmentNew extends Fragment {
// Initialize views // Initialize views
recyclerView = view.findViewById(R.id.appointments_recycler_view); recyclerView = view.findViewById(R.id.appointments_recycler_view);
fabAddAppointment = view.findViewById(R.id.fab_add_appointment); fabAddAppointment = view.findViewById(R.id.fab_add_appointment);
swipeRefresh = view.findViewById(R.id.swipe_refresh); swipeRefresh = view.findViewById(R.id.list_view_container);
typeFilterDropdown = view.findViewById(R.id.type_filter_dropdown); typeFilterDropdown = view.findViewById(R.id.type_filter_dropdown);
statusFilterDropdown = view.findViewById(R.id.status_filter_dropdown); statusFilterDropdown = view.findViewById(R.id.status_filter_dropdown);
calendarDaysContainer = view.findViewById(R.id.calendar_days_container);
calendarViewContainer = view.findViewById(R.id.calendar_view_container);
listViewContainer = view.findViewById(R.id.list_view_container);
statusBadgesContainer = view.findViewById(R.id.status_badges_container);
searchLayout = view.findViewById(R.id.search_layout);
searchInput = view.findViewById(R.id.search_input); searchInput = view.findViewById(R.id.search_input);
// Setup type filter dropdown // Setup type filter dropdown
...@@ -116,13 +135,21 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -116,13 +135,21 @@ public class AppointmentListFragmentNew extends Fragment {
filterAndDisplayAppointments(); filterAndDisplayAppointments();
}); });
// Setup search // Initialize calendar
chipConfirmed = view.findViewById(R.id.chip_confirmed);
chipAttended = view.findViewById(R.id.chip_attended);
chipHandedOver = view.findViewById(R.id.chip_handed_over);
setupCalendar();
// Setup search input
searchInput.addTextChangedListener(new TextWatcher() { searchInput.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {} public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
searchText = s.toString();
filterAndDisplayAppointments(); filterAndDisplayAppointments();
} }
...@@ -130,11 +157,11 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -130,11 +157,11 @@ public class AppointmentListFragmentNew extends Fragment {
public void afterTextChanged(Editable s) {} public void afterTextChanged(Editable s) {}
}); });
// Setup SwipeRefreshLayout // Setup SwipeRefreshLayout for List View
swipeRefresh.setColorSchemeResources(R.color.colorPrimary); swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
swipeRefresh.setOnRefreshListener(this::loadAllAppointments); swipeRefresh.setOnRefreshListener(this::loadAllAppointments);
// Setup RecyclerView // Setup RecyclerView for List View
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
adapter = new AppointmentAdapterNew(appointment -> { adapter = new AppointmentAdapterNew(appointment -> {
// Handle appointment click - navigate to details // Handle appointment click - navigate to details
...@@ -145,6 +172,7 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -145,6 +172,7 @@ public class AppointmentListFragmentNew extends Fragment {
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
}); });
adapter.setCalendarView(isCalendarViewMode); // Set initial view mode
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
// FAB to create new appointment // FAB to create new appointment
...@@ -169,9 +197,61 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -169,9 +197,61 @@ public class AppointmentListFragmentNew extends Fragment {
}); });
} }
public void toggleView() {
isCalendarViewMode = !isCalendarViewMode;
if (isCalendarViewMode) {
// Calendar view mode
calendarViewContainer.setVisibility(View.VISIBLE);
statusBadgesContainer.setVisibility(View.GONE); // Hide badges in calendar view too
searchLayout.setVisibility(View.GONE);
searchText = ""; // Clear search when switching to calendar view
} else {
// List view mode
calendarViewContainer.setVisibility(View.GONE);
statusBadgesContainer.setVisibility(View.GONE);
searchLayout.setVisibility(View.VISIBLE);
selectedDate = null; // Clear date selection when hiding calendar
}
// Recreate adapter with correct layout
adapter = new AppointmentAdapterNew(appointment -> {
// Handle appointment click - navigate to details
AppointmentDetailsFragment detailsFragment = AppointmentDetailsFragment.newInstance(appointment.getId());
requireActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container_list, detailsFragment)
.addToBackStack(null)
.commit();
});
adapter.setCalendarView(isCalendarViewMode);
recyclerView.setAdapter(adapter);
// List is always visible
listViewContainer.setVisibility(View.VISIBLE);
// Refresh appointments display
filterAndDisplayAppointments();
}
private void filterAndDisplayAppointments() { private void filterAndDisplayAppointments() {
List<AppointmentEntity> filtered = new ArrayList<>(allAppointments); List<AppointmentEntity> filtered = new ArrayList<>(allAppointments);
// Filter by search text (only in list view)
if (!isCalendarViewMode && searchText != null && !searchText.trim().isEmpty()) {
String search = searchText.toLowerCase().trim();
filtered.removeIf(appointment ->
!appointment.getUnitNo().toLowerCase().contains(search) &&
!appointment.getRepresentativeName().toLowerCase().contains(search)
);
}
// Filter by selected date from calendar (only in calendar view)
if (isCalendarViewMode && selectedDate != null) {
long selectedEpochDay = selectedDate.toEpochDay();
filtered.removeIf(appointment -> appointment.getSelectedDate() != selectedEpochDay);
}
// Filter by type // Filter by type
if (!currentTypeFilter.equals("All Type")) { if (!currentTypeFilter.equals("All Type")) {
filtered.removeIf(appointment -> !appointment.getAppointmentType().equals(currentTypeFilter)); filtered.removeIf(appointment -> !appointment.getAppointmentType().equals(currentTypeFilter));
...@@ -193,20 +273,12 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -193,20 +273,12 @@ public class AppointmentListFragmentNew extends Fragment {
} }
// If "All", don't filter by status - show everything // If "All", don't filter by status - show everything
// Filter by search text
String searchText = searchInput.getText().toString().trim().toLowerCase();
if (!searchText.isEmpty()) {
filtered.removeIf(appointment ->
!appointment.getUnitNo().toLowerCase().contains(searchText) &&
!appointment.getLocation().toLowerCase().contains(searchText)
);
}
// Sort by date, then by time // Sort by date, then by time
Collections.sort(filtered, Comparator Collections.sort(filtered, Comparator
.comparingLong(AppointmentEntity::getSelectedDate) .comparingLong(AppointmentEntity::getSelectedDate)
.thenComparing(AppointmentEntity::getSelectedTime)); .thenComparing(AppointmentEntity::getSelectedTime));
// Update adapter
adapter.setAppointments(filtered); adapter.setAppointments(filtered);
} }
...@@ -222,9 +294,6 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -222,9 +294,6 @@ public class AppointmentListFragmentNew extends Fragment {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putString(STATE_TYPE_FILTER, currentTypeFilter); outState.putString(STATE_TYPE_FILTER, currentTypeFilter);
outState.putString(STATE_STATUS_FILTER, currentStatusFilter); outState.putString(STATE_STATUS_FILTER, currentStatusFilter);
if (searchInput != null) {
outState.putString(STATE_SEARCH_TEXT, searchInput.getText().toString());
}
} }
@Override @Override
...@@ -238,6 +307,52 @@ public class AppointmentListFragmentNew extends Fragment { ...@@ -238,6 +307,52 @@ public class AppointmentListFragmentNew extends Fragment {
loadAllAppointments(); loadAllAppointments();
} }
private void setupCalendar() {
// Generate 60 days (30 before and 30 after today)
LocalDate today = LocalDate.now();
for (int i = -30; i <= 30; i++) {
LocalDate date = today.plusDays(i);
View dayView = LayoutInflater.from(requireContext()).inflate(R.layout.calendar_day_item, calendarDaysContainer, false);
TextView dateNumber = dayView.findViewById(R.id.date_number);
View selectionBackground = dayView.findViewById(R.id.selection_background);
dateNumber.setText(String.valueOf(date.getDayOfMonth()));
// Handle selection
dayView.setOnClickListener(v -> {
// Clear previous selection
for (int j = 0; j < calendarDaysContainer.getChildCount(); j++) {
View child = calendarDaysContainer.getChildAt(j);
child.findViewById(R.id.selection_background).setVisibility(View.GONE);
TextView textView = child.findViewById(R.id.date_number);
textView.setTextColor(ContextCompat.getColor(requireContext(), android.R.color.black));
}
// Set new selection
selectedDate = date;
selectionBackground.setVisibility(View.VISIBLE);
dateNumber.setTextColor(ContextCompat.getColor(requireContext(), android.R.color.white));
// Filter appointments
filterAndDisplayAppointments();
});
calendarDaysContainer.addView(dayView);
}
// Auto-scroll to today (position 30)
calendarDaysContainer.post(() -> {
View todayView = calendarDaysContainer.getChildAt(30);
if (todayView != null) {
HorizontalScrollView scrollView = (HorizontalScrollView) calendarDaysContainer.getParent();
int scrollX = todayView.getLeft() - (scrollView.getWidth() / 2) + (todayView.getWidth() / 2);
scrollView.smoothScrollTo(scrollX, 0);
}
});
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
......
package com.example.qmsmakeappointment;
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.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.time.LocalDate;
import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class CalendarDayAdapter extends RecyclerView.Adapter<CalendarDayAdapter.DayViewHolder> {
private List<LocalDate> dates = new ArrayList<>();
private LocalDate selectedDate = null;
private OnDateSelectedListener listener;
public interface OnDateSelectedListener {
void onDateSelected(LocalDate date);
}
public CalendarDayAdapter(OnDateSelectedListener listener) {
this.listener = listener;
}
public void setDates(List<LocalDate> dates) {
this.dates = dates;
notifyDataSetChanged();
}
public void setSelectedDate(LocalDate date) {
this.selectedDate = date;
notifyDataSetChanged();
}
@NonNull
@Override
public DayViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.calendar_day_item, parent, false);
return new DayViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull DayViewHolder holder, int position) {
LocalDate date = dates.get(position);
holder.bind(date, selectedDate != null && selectedDate.equals(date));
}
@Override
public int getItemCount() {
return dates.size();
}
class DayViewHolder extends RecyclerView.ViewHolder {
TextView dateNumber;
View selectionBackground;
DayViewHolder(@NonNull View itemView) {
super(itemView);
dateNumber = itemView.findViewById(R.id.date_number);
selectionBackground = itemView.findViewById(R.id.selection_background);
}
void bind(LocalDate date, boolean isSelected) {
// Set date number
dateNumber.setText(String.valueOf(date.getDayOfMonth()));
// Handle selection
if (isSelected) {
selectionBackground.setVisibility(View.VISIBLE);
dateNumber.setTextColor(Color.WHITE);
} else {
selectionBackground.setVisibility(View.GONE);
dateNumber.setTextColor(Color.BLACK);
}
// Click listener
itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onDateSelected(date);
}
});
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorPrimary"/>
<size android:width="40dp" android:height="40dp"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M3,6h18v2H3V6zM3,11h18v2H3V11zM3,16h18v2H3V16z"/>
</vector>
...@@ -42,13 +42,13 @@ ...@@ -42,13 +42,13 @@
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
<ImageButton <ImageButton
android:id="@+id/filter_button" android:id="@+id/toggle_view_button"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:background="@null" android:background="@null"
android:contentDescription="Filter" android:contentDescription="Toggle View"
android:src="@android:drawable/ic_menu_sort_by_size" android:src="@drawable/ic_menu_hamburger"
app:tint="@color/white" app:tint="@color/white"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
......
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:padding="4dp">
<!-- Background circle for selected date -->
<View
android:id="@+id/selection_background"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:background="@drawable/calendar_day_selected"
android:visibility="gone"/>
<!-- Date number -->
<TextView
android:id="@+id/day_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="15"
android:textSize="16sp"
android:textColor="@android:color/black"
android:textStyle="bold"/>
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:padding="4dp"
android:layout_marginHorizontal="2dp">
<!-- Date number with background circle -->
<FrameLayout
android:layout_width="40dp"
android:layout_height="40dp">
<!-- Selection background -->
<View
android:id="@+id/selection_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/calendar_day_selected"
android:visibility="gone"/>
<!-- Date number -->
<TextView
android:id="@+id/date_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="7"
android:textSize="16sp"
android:textColor="@android:color/black"
android:textStyle="bold"/>
</FrameLayout>
</LinearLayout>
...@@ -6,81 +6,265 @@ ...@@ -6,81 +6,265 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#F5F5F5"> android:background="#F5F5F5">
<!-- Filter Dropdowns --> <!-- Top Bar with Filters and Toggle Button -->
<LinearLayout <LinearLayout
android:id="@+id/filter_container" android:id="@+id/top_bar_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="16dp" android:padding="16dp"
android:background="@android:color/white" android:background="@android:color/white"
android:gravity="center_vertical"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputLayout <!-- Filter Dropdowns Container -->
android:id="@+id/type_filter_layout" <LinearLayout
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" android:id="@+id/filter_container"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginEnd="8dp" android:orientation="horizontal">
android:hint="Filter Type">
<AutoCompleteTextView <com.google.android.material.textfield.TextInputLayout
android:id="@+id/type_filter_dropdown" android:id="@+id/type_filter_layout"
android:layout_width="match_parent" style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="none" android:layout_weight="1"
android:text="All Type" android:layout_marginEnd="8dp"
android:textSize="14sp"/> android:hint="Filter Type">
</com.google.android.material.textfield.TextInputLayout> <AutoCompleteTextView
android:id="@+id/type_filter_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:text="All Type"
android:textSize="14sp"/>
<com.google.android.material.textfield.TextInputLayout </com.google.android.material.textfield.TextInputLayout>
android:id="@+id/status_filter_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:hint="Filter Status">
<AutoCompleteTextView <com.google.android.material.textfield.TextInputLayout
android:id="@+id/status_filter_dropdown" android:id="@+id/status_filter_layout"
android:layout_width="match_parent" style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="none" android:layout_weight="1"
android:text="Upcoming" android:layout_marginStart="8dp"
android:textSize="14sp"/> android:hint="Filter Status">
<AutoCompleteTextView
android:id="@+id/status_filter_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:text="Upcoming"
android:textSize="14sp"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
<!-- Search Bar --> <!-- Search Bar (only visible in List View) -->
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/search_layout" android:id="@+id/search_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:hint="Search Unit No. or Owner Name..."
android:visibility="gone"
app:startIconDrawable="@android:drawable/ic_menu_search" app:startIconDrawable="@android:drawable/ic_menu_search"
app:boxBackgroundColor="@android:color/white" app:layout_constraintTop_toBottomOf="@id/top_bar_container">
app:layout_constraintTop_toBottomOf="@id/filter_container">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/search_input" android:id="@+id/search_input"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="Search Unit No. or Owner Name..." android:inputType="text"
android:textSize="14sp"/> android:maxLines="1"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<!-- SwipeRefreshLayout for Pull-to-Refresh --> <!-- Status Badge Chips (only visible in Calendar View) -->
<HorizontalScrollView
android:id="@+id/status_badges_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:background="@android:color/white"
android:paddingVertical="12dp"
android:paddingHorizontal="16dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/search_layout">
<com.google.android.material.chip.ChipGroup
android:id="@+id/status_chip_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleSelection="false">
<com.google.android.material.chip.Chip
android:id="@+id/chip_confirmed"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Confirmed"
android:checkable="true"
android:checked="true"
app:chipBackgroundColor="@color/colorPrimary"
app:chipIcon="@android:drawable/checkbox_on_background"
app:chipIconTint="@android:color/white"
android:textColor="@android:color/white"/>
<com.google.android.material.chip.Chip
android:id="@+id/chip_attended"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Attended"
android:checkable="true"
android:checked="true"
app:chipBackgroundColor="#4CAF50"
app:chipIcon="@android:drawable/checkbox_on_background"
app:chipIconTint="@android:color/white"
android:textColor="@android:color/white"/>
<com.google.android.material.chip.Chip
android:id="@+id/chip_handed_over"
style="@style/Widget.Material3.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Handed Over"
android:checkable="true"
android:checked="true"
app:chipBackgroundColor="@color/colorPrimary"
app:chipIcon="@android:drawable/checkbox_on_background"
app:chipIconTint="@android:color/white"
android:textColor="@android:color/white"/>
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>
<!-- Horizontal Scrollable Calendar -->
<LinearLayout
android:id="@+id/calendar_view_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/white"
android:visibility="visible"
android:paddingVertical="12dp"
app:layout_constraintTop_toBottomOf="@id/status_badges_container">
<!-- Day Headers (Sun Mon Tue Wed Thu Fri Sat) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="8dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Sun"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Mon"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Tue"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Wed"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Thu"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Fri"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Sat"
android:textAlignment="center"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#666666"/>
</LinearLayout>
<!-- Horizontal Calendar Row (ONE WEEK at a time) -->
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:id="@+id/calendar_days_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="8dp"/>
</HorizontalScrollView>
</LinearLayout>
<!-- Appointments List -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh" android:id="@+id/list_view_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/search_layout" android:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/calendar_view_container"
app:layout_constraintBottom_toBottomOf="parent"> app:layout_constraintBottom_toBottomOf="parent">
<!-- RecyclerView for Appointments List --> <!-- RecyclerView for Appointments List -->
......
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#F5F5F5"
android:paddingVertical="4dp">
<!-- Date Header on LEFT (only shown for first item of each date) -->
<LinearLayout
android:id="@+id/date_header_container"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:paddingHorizontal="8dp"
android:paddingTop="8dp"
android:visibility="gone">
<TextView
android:id="@+id/date_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="11"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#000000"/>
<TextView
android:id="@+id/date_weekday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Thu"
android:textSize="12sp"
android:textColor="#666666"/>
</LinearLayout>
<!-- Spacer when no date header -->
<View
android:id="@+id/date_spacer"
android:layout_width="60dp"
android:layout_height="1dp"
android:visibility="visible"/>
<!-- Appointment Card -->
<androidx.cardview.widget.CardView
android:id="@+id/appointment_card"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp"
app:cardBackgroundColor="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- Colored Left Border -->
<View
android:id="@+id/appointment_color_bar"
android:layout_width="4dp"
android:layout_height="match_parent"
android:background="@color/colorPrimary"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<!-- Left Section: Time and Badge -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginEnd="24dp">
<!-- Time -->
<TextView
android:id="@+id/appointment_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="02:30 PM"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#000000"
android:layout_marginBottom="8dp"/>
<!-- Type Badge -->
<TextView
android:id="@+id/appointment_type_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/badge_handover"
android:paddingHorizontal="10dp"
android:paddingVertical="3dp"
android:text="Handover"
android:textColor="@android:color/white"
android:textSize="13sp" />
</LinearLayout>
<!-- Right Section -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<!-- Unit Number -->
<TextView
android:id="@+id/appointment_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A-3-11"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#000000"
android:layout_marginBottom="4dp"/>
<!-- Owner Name -->
<TextView
android:id="@+id/appointment_location"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="John Anthony Snow"
android:textSize="14sp"
android:textColor="#666666"
android:maxLines="1"
android:ellipsize="end"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
android:orientation="vertical" android:orientation="vertical"
android:background="#F5F5F5"> android:background="#F5F5F5">
<!-- Date Header (only shown for first item of each date) --> <!-- Date Header at TOP (only shown for first item of each date) -->
<TextView <TextView
android:id="@+id/date_header" android:id="@+id/date_header"
android:layout_width="match_parent" android:layout_width="match_parent"
...@@ -33,8 +33,20 @@ ...@@ -33,8 +33,20 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal">
android:padding="16dp">
<!-- Colored Left Border -->
<View
android:id="@+id/appointment_color_bar"
android:layout_width="4dp"
android:layout_height="match_parent"
android:background="@color/colorPrimary"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<!-- Left Section: Time and Badge --> <!-- Left Section: Time and Badge -->
<LinearLayout <LinearLayout
...@@ -112,6 +124,8 @@ ...@@ -112,6 +124,8 @@
</LinearLayout> </LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
</LinearLayout> </LinearLayout>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment