unit View.ComplaintDetails;

interface

uses
  System.SysUtils, System.Classes, JS, Web,
  WEBLib.Forms, WEBLib.Dialogs, WEBLib.Graphics,
  XData.Web.Client,
  ConnectionModule,
  Utils, Vcl.Controls, WEBLib.Controls, WEBLib.Grids, WEBLib.DBCtrls,
  Data.DB, WEBLib.DB, XData.Web.JsonDataset, XData.Web.Dataset,
  Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.Lists;

type
  TFViewComplaintDetails = class(TWebForm)
    xdwcComplaintDetails: TXDataWebClient;
    xdwdsRemarks: TXDataWebDataSet;
    wdsRemarks: TWebDataSource;
    xdwdsRemarksMemoType: TStringField;
    xdwdsRemarksTimestamp: TStringField;
    xdwdsRemarksRemarks: TStringField;
    xdwdsRemarksBadgeNumber: TStringField;
    xdwdsRemarksMemoId: TStringField;
    xdwdsRemarksCFSId: TStringField;
    xdwdsHistory: TXDataWebDataSet;
    wdsHistory: TWebDataSource;
    xdwdsHistoryApartment: TStringField;
    xdwdsHistoryComplaint: TStringField;
    xdwdsHistoryDateReported: TStringField;
    xdwdsHistoryDPriority: TStringField;
    xdwdsHistoryDCallType: TStringField;
    xdwdsContacts: TXDataWebDataSet;
    wdsContacts: TWebDataSource;
    xdwdsContactsName: TStringField;
    xdwdsContactsPhone: TStringField;
    xdwdsContactsDContactType: TStringField;
    xdwdsContactsRemarks: TStringField;
    xdwdsWarnings: TXDataWebDataSet;
    wdsWarnings: TWebDataSource;
    xdwdsWarningsCodeDesc: TStringField;
    xdwdsWarningsAddress: TStringField;
    xdwdsWarningsNotes: TStringField;
    tblRemarks: TWebDBTableControl;
    tblContacts: TWebDBTableControl;
    tblHistory: TWebDBTableControl;
    tblWarnings: TWebDBTableControl;
    btnRemarks: TWebButton;
    btnHistory: TWebButton;
    btnWarnings: TWebButton;
    btnContacts: TWebButton;
    btnCmp: TWebButton;
    btnE911: TWebButton;
    btnREM: TWebButton;
    btnUnt: TWebButton;
    lstWarnings: TWebDBListControl;
    btnComplaintViewOnMap: TWebButton;

    procedure btnRemarksClick(Sender: TObject);
    procedure btnHistoryClick(Sender: TObject);
    procedure btnContactsClick(Sender: TObject);
    procedure btnWarningsClick(Sender: TObject);
    procedure btnCmpClick(Sender: TObject);
    procedure btnComplaintViewOnMapClick(Sender: TObject);
    procedure btnE911Click(Sender: TObject);
    procedure btnREMClick(Sender: TObject);
    procedure btnUntClick(Sender: TObject);
  private
    FComplaintId: string;
    FCfsId: string;
    FLoading: Boolean;

    FAllMemos: TJSArray;

    FShowCmp: Boolean;
    FShowE911: Boolean;
    FShowRem: Boolean;
    FShowUnt: Boolean;

    FHasHistory: Boolean;
    FHasContacts: Boolean;
    FHasWarnings: Boolean;

    FHistoryLoaded: Boolean;
    FContactsLoaded: Boolean;
    FWarningsLoaded: Boolean;

    procedure WireUi;
    procedure CloseClick(Event: TJSEvent);

    procedure SetTextById(const elementId, value: string);
    function MemoTypeInList(const memoType: string; const list: array of string): Boolean;
    function MemoTypeLabel(const memoTypeCode: string): string;
    function DeriveStatusFromDates(const dateDispatched, dateArrived, dateCleared: string): string;

    procedure SetDataSetJsonData(dataSet: TXDataWebDataSet; dataArr: TJSArray);

    procedure SetHiddenById(const elementId: string; hidden: Boolean);
    procedure SetActiveTab(const tabName: string);
    procedure UpdateToggleButtonCss(const buttonId: string; isOn: Boolean);
    procedure UpdateTabButtonCss(const activeTabName: string);
    procedure SetButtonEnabledById(const buttonId: string; enabled: Boolean);

    procedure ApplyRemarksFilters;

    [async] procedure ApplyTabAsync(const tabName: string);

    [async] procedure LoadComplaintAsync;
    [async] procedure LoadMemosAsync;
    [async] procedure LoadHistoryAsync;
    [async] procedure LoadContactsAsync;
    [async] procedure LoadWarningsAsync;
  public
    class function CreateForm(AElementID, ComplaintId: string): TWebForm;
    procedure InitializeForm;
  end;

var
  FViewComplaintDetails: TFViewComplaintDetails;

implementation

uses
  View.Main,
  View.Complaints;

{$R *.dfm}

class function TFViewComplaintDetails.CreateForm(AElementID, ComplaintId: string): TWebForm;

  procedure AfterCreate(AForm: TObject);
  begin
    with TFViewComplaintDetails(AForm) do
    begin
      FComplaintId := ComplaintId;
      InitializeForm;
    end;
  end;

begin
  Application.CreateForm(TFViewComplaintDetails, AElementID, Result, @AfterCreate);
end;

procedure TFViewComplaintDetails.InitializeForm;
begin
  xdwcComplaintDetails.Connection := DMConnection.ApiConnection;

  xdwdsRemarks.Connection := DMConnection.ApiConnection;
  xdwdsHistory.Connection := DMConnection.ApiConnection;
  xdwdsContacts.Connection := DMConnection.ApiConnection;
  xdwdsWarnings.Connection := DMConnection.ApiConnection;

  FShowCmp := True;
  FShowE911 := True;
  FShowRem := True;
  FShowUnt := True;

  FHasHistory := True;
  FHasContacts := True;
  FHasWarnings := True;

  FHistoryLoaded := False;
  FContactsLoaded := False;
  FWarningsLoaded := False;

  WireUi;
  LoadComplaintAsync;
end;

procedure TFViewComplaintDetails.WireUi;
var
  btnClose: TJSElement;
begin
  btnClose := Document.getElementById('btn_close_complaint_details');
  if btnClose <> nil then
    btnClose.addEventListener('click', @CloseClick);
end;

procedure TFViewComplaintDetails.CloseClick(Event: TJSEvent);
begin
  Event.preventDefault;
  if Assigned(FViewMain) then
  begin
    FViewMain.SetActiveNavButton('view.main.btncomplaints');
    FViewMain.ShowForm(TFViewComplaints);
  end;
end;

procedure TFViewComplaintDetails.SetTextById(const elementId, value: string);
var
  el: TJSElement;
begin
  el := Document.getElementById(elementId);
  if el <> nil then
    TJSHtmlElement(el).innerText := value;
end;

function TFViewComplaintDetails.MemoTypeInList(const memoType: string; const list: array of string): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := Low(list) to High(list) do
  begin
    if memoType = list[i] then
      Exit(True);
  end;
end;

function TFViewComplaintDetails.MemoTypeLabel(const memoTypeCode: string): string;
begin
  if MemoTypeInList(memoTypeCode, ['30', '31', '32', '33', '34']) then
    Exit('UNT');
  if MemoTypeInList(memoTypeCode, ['3', '4']) then
    Exit('CMP');
  if MemoTypeInList(memoTypeCode, ['2']) then
    Exit('E-911');
  if MemoTypeInList(memoTypeCode, ['1']) then
    Exit('REM');
  Result := memoTypeCode;
end;

function TFViewComplaintDetails.DeriveStatusFromDates(const dateDispatched, dateArrived, dateCleared: string): string;
begin
  if Trim(dateCleared) <> '' then
    Exit('Cleared');
  if Trim(dateArrived) <> '' then
    Exit('On Scene');
  if Trim(dateDispatched) <> '' then
    Exit('Dispatched');
  Result := 'Pending';
end;

procedure TFViewComplaintDetails.SetDataSetJsonData(dataSet: TXDataWebDataSet; dataArr: TJSArray);
begin
  dataSet.Close;
  dataSet.SetJsonData(dataArr);
  dataSet.Open;
end;

procedure TFViewComplaintDetails.SetHiddenById(const elementId: string; hidden: Boolean);
var
  el: TJSElement;
begin
  el := Document.getElementById(elementId);
  if el = nil then
    Exit;

  if hidden then
    TJSElement(el).classList.add('tab-hidden')
  else
    TJSElement(el).classList.remove('tab-hidden');
end;

procedure TFViewComplaintDetails.UpdateToggleButtonCss(const buttonId: string; isOn: Boolean);
var
  el: TJSElement;
  btn: TJSHTMLButtonElement;
begin
  el := Document.getElementById(buttonId);
  if el = nil then
    Exit;

  btn := TJSHTMLButtonElement(el);

  btn.classList.remove('btn-success');
  btn.classList.remove('btn-secondary');
  btn.classList.remove('active');

  if isOn then
  begin
    btn.classList.add('btn-success');
    btn.classList.add('active');
    btn.setAttribute('aria-pressed', 'true');
  end
  else
  begin
    btn.classList.add('btn-secondary');
    btn.setAttribute('aria-pressed', 'false');
  end;
end;

procedure TFViewComplaintDetails.UpdateTabButtonCss(const activeTabName: string);
var
  tabRemarks: TJSElement;
  tabHistory: TJSElement;
  tabContacts: TJSElement;
  tabWarnings: TJSElement;

  procedure SetTab(el: TJSElement; isActive: Boolean);
  var
    btn: TJSHTMLButtonElement;
  begin
    if el = nil then
      Exit;

    btn := TJSHTMLButtonElement(el);

    btn.classList.remove('active');
    if isActive then
    begin
      btn.classList.add('active');
      btn.setAttribute('aria-pressed', 'true');
    end
    else
      btn.setAttribute('aria-pressed', 'false');
  end;

begin
  tabRemarks := Document.getElementById('btn_remarks');
  tabHistory := Document.getElementById('btn_history');
  tabContacts := Document.getElementById('btn_contacts');
  tabWarnings := Document.getElementById('btn_warnings');

  SetTab(tabRemarks, activeTabName = 'remarks');
  SetTab(tabHistory, activeTabName = 'history');
  SetTab(tabContacts, activeTabName = 'contacts');
  SetTab(tabWarnings, activeTabName = 'warnings');
end;

procedure TFViewComplaintDetails.SetButtonEnabledById(const buttonId: string; enabled: Boolean);
var
  el: TJSElement;
  btn: TJSHTMLButtonElement;
begin
  el := Document.getElementById(buttonId);
  if el = nil then
    Exit;

  btn := TJSHTMLButtonElement(el);

  btn.disabled := not enabled;

  btn.classList.remove('disabled');
  btn.classList.remove('btn-success');
  btn.classList.remove('btn-secondary');

  if enabled then
  begin
    btn.classList.add('btn-success');
  end
  else
  begin
    btn.classList.add('btn-secondary');
    btn.classList.add('disabled');
    btn.classList.remove('active');
    btn.setAttribute('aria-pressed', 'false');
  end;
end;

procedure TFViewComplaintDetails.SetActiveTab(const tabName: string);
begin
  SetHiddenById('tbl_remarks', tabName <> 'remarks');
  SetHiddenById('tbl_history', tabName <> 'history');
  SetHiddenById('tbl_contacts', tabName <> 'contacts');

  SetHiddenById('tbl_warnings', tabName <> 'warnings');
  SetHiddenById('lst_warnings', tabName <> 'warnings');

  SetHiddenById('row_remarks_toggles', tabName <> 'remarks');

  UpdateTabButtonCss(tabName);
end;


procedure TFViewComplaintDetails.ApplyRemarksFilters;
var
  filteredArr: TJSArray;
  i: Integer;
  rowObj: TJSObject;
  memoType: string;
  showRow: Boolean;
begin
  if not Assigned(FAllMemos) then
    Exit;

  filteredArr := TJSArray.new;

  for i := 0 to Integer(FAllMemos.length) - 1 do
  begin
    rowObj := TJSObject(FAllMemos[i]);
    memoType := string(rowObj['MemoTypeCode']);

    showRow := False;

    if FShowCmp and MemoTypeInList(memoType, ['3', '4']) then
      showRow := True;

    if FShowE911 and MemoTypeInList(memoType, ['2']) then
      showRow := True;

    if FShowRem and MemoTypeInList(memoType, ['1']) then
      showRow := True;

    if FShowUnt and MemoTypeInList(memoType, ['30', '31', '32', '33', '34']) then
      showRow := True;

    if showRow then
      filteredArr.push(rowObj);
  end;

  SetDataSetJsonData(xdwdsRemarks, filteredArr);

  UpdateToggleButtonCss('btn_cmp', FShowCmp);
  UpdateToggleButtonCss('btn_e911', FShowE911);
  UpdateToggleButtonCss('btn_rem', FShowRem);
  UpdateToggleButtonCss('btn_unt', FShowUnt);
end;

[async] procedure TFViewComplaintDetails.ApplyTabAsync(const tabName: string);
begin
  if tabName = 'remarks' then
  begin
    SetActiveTab('remarks');
    ApplyRemarksFilters;
    Exit;
  end;

  if tabName = 'history' then
  begin
    if not FHasHistory then
      Exit;

    SetActiveTab('history');

    if not FHistoryLoaded then
    begin
      ShowSpinner('spinner');
      try
        await(LoadHistoryAsync);
      finally
        HideSpinner('spinner');
      end;
    end;

    Exit;
  end;

  if tabName = 'contacts' then
  begin
    if not FHasContacts then
      Exit;

    SetActiveTab('contacts');

    if not FContactsLoaded then
    begin
      ShowSpinner('spinner');
      try
        await(LoadContactsAsync);
      finally
        HideSpinner('spinner');
      end;
    end;

    Exit;
  end;

  if tabName = 'warnings' then
  begin
    if not FHasWarnings then
      Exit;

    SetActiveTab('warnings');

    if not FWarningsLoaded then
    begin
      ShowSpinner('spinner');
      try
        await(LoadWarningsAsync);
      finally
        HideSpinner('spinner');
      end;
    end;

    Exit;
  end;
end;

procedure TFViewComplaintDetails.btnRemarksClick(Sender: TObject);
begin
  ApplyTabAsync('remarks');
end;

procedure TFViewComplaintDetails.btnHistoryClick(Sender: TObject);
begin
  ApplyTabAsync('history');
end;

procedure TFViewComplaintDetails.btnContactsClick(Sender: TObject);
begin
  ApplyTabAsync('contacts');
end;

procedure TFViewComplaintDetails.btnWarningsClick(Sender: TObject);
begin
  ApplyTabAsync('warnings');
end;

procedure TFViewComplaintDetails.btnCmpClick(Sender: TObject);
begin
  FShowCmp := not FShowCmp;
  ApplyRemarksFilters;
end;

procedure TFViewComplaintDetails.btnComplaintViewOnMapClick(Sender: TObject);
begin
  if Assigned(FViewMain) then
    FViewMain.ShowMapFocusComplaint(FComplaintId);
end;

procedure TFViewComplaintDetails.btnE911Click(Sender: TObject);
begin
  FShowE911 := not FShowE911;
  ApplyRemarksFilters;
end;

procedure TFViewComplaintDetails.btnREMClick(Sender: TObject);
begin
  FShowRem := not FShowRem;
  ApplyRemarksFilters;
end;

procedure TFViewComplaintDetails.btnUntClick(Sender: TObject);
begin
  FShowUnt := not FShowUnt;
  ApplyRemarksFilters;
end;

[async] procedure TFViewComplaintDetails.LoadComplaintAsync;
var
  resp: TXDataClientResponse;
  rootObj: TJSObject;
  dataObj: TJSObject;

  complaintText: string;
  priorityText: string;
  statusText: string;
  dispatchDescText: string;
  dispatchDistrictText: string;
  addressText: string;

  dateDispatchedText: string;
  dateArrivedText: string;
  dateClearedText: string;

  historyFlag: string;
  contactsFlag: string;
  warningsFlag: string;
begin
  if FLoading then
    Exit;

  FLoading := True;
  ShowSpinner('spinner');
  try
    try
      resp := await(xdwcComplaintDetails.RawInvokeAsync('IApiService.GetComplaintDetails', [FComplaintId]));
      rootObj := TJSObject(resp.Result);
      dataObj := TJSObject(rootObj['data']);

      complaintText := string(dataObj['Complaint']);
      priorityText := string(dataObj['Priority']);
      dispatchDescText := string(dataObj['DispatchCodeDesc']);
      dispatchDistrictText := string(dataObj['DispatchDistrict']);
      addressText := string(dataObj['Address']);
      FCfsId := string(dataObj['CFSId']);

      statusText := '';
      if dataObj['Status'] <> nil then
        statusText := string(dataObj['Status']);

      dateDispatchedText := '';
      dateArrivedText := '';
      dateClearedText := '';

      if dataObj['DateDispatched'] <> nil then
        dateDispatchedText := string(dataObj['DateDispatched']);
      if dataObj['DateArrived'] <> nil then
        dateArrivedText := string(dataObj['DateArrived']);
      if dataObj['DateCleared'] <> nil then
        dateClearedText := string(dataObj['DateCleared']);

      if Trim(statusText) = '' then
        statusText := DeriveStatusFromDates(dateDispatchedText, dateArrivedText, dateClearedText);
     // Note: reenable if using the complaint in list html that is currently commented out
//      SetTextById('lbl_complaint_number', complaintText);
      SetTextById('lbl_summary_title', 'Summary for Complaint ' + complaintText);
      SetTextById('lbl_priority', priorityText);
      SetTextById('lbl_status', statusText);
      SetTextById('lbl_dispatch_code', dispatchDescText);
      SetTextById('lbl_dispatch_district', dispatchDistrictText);
      SetTextById('lbl_address', addressText);

      historyFlag := '';
      contactsFlag := '';
      warningsFlag := '';

      if dataObj['History'] <> nil then
        historyFlag := string(dataObj['History']);
      if dataObj['Contacts'] <> nil then
        contactsFlag := string(dataObj['Contacts']);
      if dataObj['Warnings'] <> nil then
        warningsFlag := string(dataObj['Warnings']);

      FHasHistory := historyFlag <> '-1';
      FHasContacts := contactsFlag <> '-1';
      FHasWarnings := warningsFlag <> '-1';

      SetButtonEnabledById('btn_history', FHasHistory);
      SetButtonEnabledById('btn_contacts', FHasContacts);
      SetButtonEnabledById('btn_warnings', FHasWarnings);

      await(LoadMemosAsync);

      SetActiveTab('remarks');
      ApplyRemarksFilters;
    except
      on E: EXDataClientRequestException do
        Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
      on E: Exception do
        Utils.ShowErrorModal(E.Message);
    end;
  finally
    HideSpinner('spinner');
    FLoading := False;
  end;
end;

[async] procedure TFViewComplaintDetails.LoadMemosAsync;
var
  resp: TXDataClientResponse;
  rootObj: TJSObject;
  dataArr: TJSArray;
  i: Integer;
  rowObj: TJSObject;
  memoTypeCode: string;
begin
  if Trim(FCfsId) = '' then
    Exit;

  resp := await(xdwcComplaintDetails.RawInvokeAsync('IApiService.GetComplaintMemos', [FCfsId]));
  rootObj := TJSObject(resp.Result);

  dataArr := TJSArray(rootObj['data']);
  FAllMemos := dataArr;

  for i := 0 to Integer(dataArr.length) - 1 do
  begin
    rowObj := TJSObject(dataArr[i]);
    memoTypeCode := string(rowObj['MemoType']);
    rowObj['MemoTypeCode'] := memoTypeCode;
    rowObj['MemoType'] := MemoTypeLabel(memoTypeCode);
  end;

  SetDataSetJsonData(xdwdsRemarks, dataArr);
end;

[async] procedure TFViewComplaintDetails.LoadHistoryAsync;
var
  resp: TXDataClientResponse;
  rootObj: TJSObject;
  dataArr: TJSArray;
begin
  resp := await(xdwcComplaintDetails.RawInvokeAsync('IApiService.GetComplaintHistory', [FComplaintId]));
  rootObj := TJSObject(resp.Result);

  dataArr := TJSArray(rootObj['data']);
  SetDataSetJsonData(xdwdsHistory, dataArr);

  FHistoryLoaded := True;
end;

[async] procedure TFViewComplaintDetails.LoadContactsAsync;
var
  resp: TXDataClientResponse;
  rootObj: TJSObject;
  dataArr: TJSArray;
begin
  resp := await(xdwcComplaintDetails.RawInvokeAsync('IApiService.GetComplaintContacts', [FComplaintId]));
  rootObj := TJSObject(resp.Result);

  dataArr := TJSArray(rootObj['data']);
  SetDataSetJsonData(xdwdsContacts, dataArr);

  FContactsLoaded := True;
end;

[async] procedure TFViewComplaintDetails.LoadWarningsAsync;
var
  resp: TXDataClientResponse;
  rootObj: TJSObject;
  dataArr: TJSArray;
begin
  resp := await(xdwcComplaintDetails.RawInvokeAsync('IApiService.GetComplaintWarnings', [FComplaintId]));
  rootObj := TJSObject(resp.Result);

  dataArr := TJSArray(rootObj['data']);
  SetDataSetJsonData(xdwdsWarnings, dataArr);

  FWarningsLoaded := True;
end;

end.

