Email CC Expriment

Email CC Expriment

Support Request Modify

@page "/supportrequestmodify/{id}"
@inject ISupportRequestService _ISupportRequestService

<MudText Typo="Typo.h4">Modify Support Request</MudText>

<EditForm Model="@_SupportRequest" OnValidSubmit="SupportRequestModifyPage">
    <DataAnnotationsValidator />
    <MudGrid>
        <MudItem xs="12" sm="7">
            <MudCard>
                <MudCardContent>
                    <MudTextField Label="Request No" @bind-Value="_SupportRequest.RequestNo"
                        For="@(() => _SupportRequest.RequestNo)" ReadOnly="true" Variant="Variant.Filled" />

                    <MudTextField Label="User Name" @bind-Value="_SupportRequest.UserName"
                        For="@(() => _SupportRequest.UserName)" ReadOnly="true" Variant="Variant.Filled" />

                    <MudSelect T="string" Dense="true" Label="Select Type" Placeholder="Select Type"
                        @bind-Value="_SupportRequest.TypeCode" For="@(() => _SupportRequest.TypeCode)"
                        Validation="@(_SupportRequest.TypeCode)">
                        @foreach (var listdata2 in supportcombolists.Item1)
                        {
                            <MudSelectItem Value="listdata2.TypeCode">@listdata2.TypeName</MudSelectItem>
                        }
                    </MudSelect>

                    <MudSelect T="string" Dense="true" Label="Select Status" Placeholder="Select Status"
                        @bind-Value="_SupportRequest.StatusCode" For="@(() => _SupportRequest.StatusCode)"
                        Validation="@(_SupportRequest.StatusCode)">
                        @foreach (var listdata3 in supportcombolists.Item2)
                        {
                            <MudSelectItem Value="listdata3.StatusCode">@listdata3.StatusName</MudSelectItem>
                        }
                    </MudSelect>

                    <MudTextField Label="Subject" Class="mt-3" Lines="2" @bind-Value="_SupportRequest.Subject"
                        For="@(() => _SupportRequest.Subject)" />

                    <MudTextField Label="Description" Class="mt-3" Lines="3" @bind-Value="_SupportRequest.Notes"
                        For="@(() => _SupportRequest.Notes)" />

                    <MudTextField Label="User Mobile No" @bind-Value="_SupportRequest.MobileNo" 
                    For="@(() => _SupportRequest.MobileNo)" HelperText="In case we want to contact you..." />            

                    @if (userType == "S")
                    {
                        <MudTextField Label="Suggestions"
                        Lines="5" @bind-Value="_SupportRequest.SystemNotes"
                        For="@(() => _SupportRequest.SystemNotes)" AutoGrow />

                        @* <MudTextField Label="CC Email" Placeholder="Enter CC Emails separated by semicolon (;)"
                        Lines="5" @bind-Value="_SupportRequest.EmailCc"
                        For="@(() => _SupportRequest.EmailCc)" AutoGrow /> *@
                        <div style="position: relative;">
                            <MudTextField T="string"
                            Label="CC Email"
                            Immediate="true"
                            Placeholder="Enter CC Emails separated by semicolon (;)"
                            Value="_SupportRequest.EmailCc"
                            ValueChanged="HandleCcInput"
                            OnKeyDown="HandleCcKeyPress"
                            Lines="3"
                            style="width: 100%; padding: 12px; font-size: 1rem; font-family: inherit;" />
                        <div class="ghost-suggestion">@GetCcSuggestionOverlay()</div>

                        </div>
                    }

                </MudCardContent>
                <MudCardActions>
                    <MudStack Row="true">
                        <MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary"
                            Class="ml-auto" StartIcon="@Icons.Material.Filled.Save">Save</MudButton>
                        <MudButton Variant="Variant.Filled" Color="Color.Default" OnClick="Cancel"
                            StartIcon="@Icons.Material.Filled.Cancel">Cancel</MudButton>
                    </MudStack>
                </MudCardActions>
            </MudCard>
        </MudItem>
    </MudGrid>
</EditForm>


@code
{
    SupportRequest _SupportRequest = new ();
    [Parameter]
    public string id { get; set; } = string.Empty;
    Tuple<List<SupportRequest>, List<SupportRequest>> supportcombolists =
    new Tuple<List<SupportRequest>, List<SupportRequest>>
    (new List<SupportRequest>(), new List<SupportRequest>());
    PostLogin _PostLogin = new ();
    public string userType { get; set; } = string.Empty;
    public string lastEmailCc = "";
    public string? ccPrediction;
    List<string> knownEmails = new(); // already loaded in OnInit


    protected override async Task OnInitializedAsync()
    {
        _PostLogin = await _LocalSession.GetItemAsync<PostLogin>("userinfo");
        var rolecnt = _IPostLoginService.GetUserRolesCnt(_PostLogin.userdb, "SupportRequest", _PostLogin.dbname);
        var usersessionid = _IPostLoginService.GetUserSessionId(_PostLogin.userdb);
        usersessionid = usersessionid.Replace("\"", "");

        if (_PostLogin.BrowserSessionId != usersessionid)
        {
            NavManager.NavigateTo("/invaliduser/5");
        }
        else if (rolecnt > 0)
        {
            userType = _IPostLoginService.GetUserType(_PostLogin.userdb).Replace("\"", "");
            supportcombolists = await _ISupportRequestService.SupportRequestCombo();
            _SupportRequest = await _ISupportRequestService.SupportRequestDetails(Convert.ToInt32(id));
            knownEmails = await _ISupportRequestService.GetDistinctEmails(); 
        }
        else
        {
            NavManager.NavigateTo("/accessdenied/1");
        }
    }

    public async Task SupportRequestModifyPage()
    {        
        await _ISupportRequestService.SupportRequestModify(_SupportRequest);
        NavManager.NavigateTo($"/supportrequestdetails/{Convert.ToInt32(id)}");
    }

    public void Cancel() => NavManager.NavigateTo($"/supportrequestdetails/{Convert.ToInt32(id)}");
    private void HandleCcInput(string value)
    {
        if (value == lastEmailCc) return;

        lastEmailCc = value;
        _SupportRequest.EmailCc = value;
        ccPrediction = null;

        var tokens = value.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
        var lastEmail = tokens.LastOrDefault()?.Trim() ?? "";

        if (!string.IsNullOrWhiteSpace(lastEmail))
        {
            var match = knownEmails
                .FirstOrDefault(email =>
                    email.StartsWith(lastEmail, StringComparison.OrdinalIgnoreCase) &&
                    !string.Equals(email, lastEmail, StringComparison.OrdinalIgnoreCase));

            if (!string.IsNullOrWhiteSpace(match))
            {
                ccPrediction = match;
            }
        }
    }

    private void HandleCcKeyPress(KeyboardEventArgs e)
    {
        if (e.Key == "ArrowRight" && !string.IsNullOrWhiteSpace(ccPrediction))
        {
            var parts = _SupportRequest.EmailCc.Split(';', StringSplitOptions.TrimEntries).ToList();

            if (parts.Count > 0)
            {
                // Replace the last incomplete email with prediction
                parts[^1] = ccPrediction!;
                _SupportRequest.EmailCc = string.Join(";", parts) + ";";
                StateHasChanged();
                lastEmailCc = _SupportRequest.EmailCc;
            }

            ccPrediction = null;
        }
    }

    private string GetCcSuggestionOverlay()
    {
        if (_SupportRequest.EmailCc == null || ccPrediction == null)
            return string.Empty;

        var tokens = _SupportRequest.EmailCc.Split(';', StringSplitOptions.None);
        var lastEmail = tokens.LastOrDefault() ?? "";

        if (!string.IsNullOrWhiteSpace(lastEmail) &&
            ccPrediction.StartsWith(lastEmail, StringComparison.OrdinalIgnoreCase))
        {
            var suffix = ccPrediction.Substring(lastEmail.Length);
            return _SupportRequest.EmailCc + suffix;
        }

        return string.Empty;
    }




}

<style>
    .ghost-suggestion {
        position: absolute;
        top: 31px; /* Adjust based on padding of input */
        left: 12px;
        font-size: 1rem; /* Match input font */
        font-family: inherit;
        color: gray;
        opacity: 0.5;
        white-space: pre;
        pointer-events: none;
        z-index: 1;
    }
</style>

Support Request Services

Task<List<string>> GetDistinctEmails();
public async Task<List<string>> GetDistinctEmails()
{
    return await _HttpClient.GetFromJsonAsync<List<string>>($"api/supportrequest/getdistinctemails")?? new List<string>();
}

Support Request Controller

[HttpGet]
public async Task<ActionResult> GetDistinctEmails()
{
    return Ok(await _ISupportRequestRepository.GetDistinctEmails()); 
}

Support Request Repository

Task<List<string>> GetDistinctEmails();
public async Task<List<string>> GetDistinctEmails()
{
    var query = @"SELECT DISTINCT UserEmail FROM SupportRequest 
    WHERE Project ='MFG' AND UserEmail IS NOT NULL";

    using var connection = _DapperContext.SetCommonConnection();
    var data = await connection.QueryAsync<string>(query);
    return data.ToList();
}