Skip to content

Commit

Permalink
Merge pull request #84 from Avanade/80-filter-by-request-type
Browse files Browse the repository at this point in the history
feat: Filter by request type
  • Loading branch information
iibuan authored Aug 29, 2024
2 parents ebca4f4 + 62cdecd commit fd9ac35
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/goapp/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func setPageRoutes() {
}

func setApiRoutes() {

httpRouter.GET("/api/request/types", m.Chain(rtApi.GetRequestTypes, m.AzureAuth()))
httpRouter.POST("/api/request", rtApprovals.ApprovalRequestHandler)
httpRouter.POST("/api/process", rtApprovals.ProcessResponseHandler)
httpRouter.GET("/api/items/type/{type:[0-2]+}/status/{status:[0-3]+}", m.Chain(rtApi.GetItems, m.AzureAuth()))
Expand Down
61 changes: 59 additions & 2 deletions src/goapp/routes/apis/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ func GetItems(w http.ResponseWriter, r *http.Request) {
filter, _ := strconv.Atoi(params["filter"][0])
offset, _ := strconv.Atoi(params["offset"][0])
search := params["search"][0]
data, total, err := GetItemsBy(ItemType(itemType), ItemStatus(itemStatus), user, search, offset, filter)
requestType := ""
if params["requestType"] != nil {
if params["requestType"][0] != "" {
requestType = params["requestType"][0]
}
}

data, total, err := GetItemsBy(ItemType(itemType), ItemStatus(itemStatus), requestType, user, search, offset, filter)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
Expand All @@ -111,7 +118,7 @@ func GetItems(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(result)
}

func GetItemsBy(itemType ItemType, itemStatus ItemStatus, user, search string, offset, filter int) ([]Item, int, error) {
func GetItemsBy(itemType ItemType, itemStatus ItemStatus, requestType, user, search string, offset, filter int) ([]Item, int, error) {
dbConnectionParam := sql.ConnectionParam{
ConnectionString: os.Getenv("APPROVALSYSTEMDB_CONNECTION_STRING"),
}
Expand All @@ -129,6 +136,9 @@ func GetItemsBy(itemType ItemType, itemStatus ItemStatus, user, search string, o
params["User"] = user
}

if requestType != "" {
params["RequestType"] = requestType
}
params["IsApproved"] = itemStatus
params["Search"] = search

Expand Down Expand Up @@ -212,3 +222,50 @@ func GetItemsBy(itemType ItemType, itemStatus ItemStatus, user, search string, o

return items, total, nil
}

type RequestType struct {
Id string `json:"id"`
Name string `json:"name"`
}

func GetRequestTypes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

result, err := GetRequestType()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(result)
}

func GetRequestType() ([]RequestType, error) {
dbConnectionParam := sql.ConnectionParam{
ConnectionString: os.Getenv("APPROVALSYSTEMDB_CONNECTION_STRING"),
}

db, err := sql.Init(dbConnectionParam)
if err != nil {
return nil, err
}
defer db.Close()

result, err := db.ExecuteStoredProcedureWithResult("PR_ApplicationModules_Select", nil)
if err != nil {
return nil, err
}

var requestTypes []RequestType

for _, v := range result {
requestType := RequestType{
Id: fmt.Sprintf("%v", v["Id"]),
Name: fmt.Sprintf("%v", v["Name"]),
}
requestTypes = append(requestTypes, requestType)
}

return requestTypes, nil
}
60 changes: 55 additions & 5 deletions src/goapp/templates/myapprovals.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,42 @@
</nav>
<div x-show="activeTab == tabs[0]">
<div x-data="list({
enabledSearch: false,
otherState: { responseType: 'All', requestType: {id : '', name: 'All'} },
callback: pendingCallback,
renderItem: pendingRenderItem
})">
<nav class="bg-white flex items-center justify-between" aria-label="header">
<div class="sm:block">
<div class="content-start">
<label for="filter" class="block text-sm font-medium text-gray-700">Filter by Request Type</label>
<select @change="(e) => {
state.other.requestType = requestTypes.find((obj) => { return e.target.value == obj.name})
load()
}"
id="reponseType" name="responseType" class="block mt-1 pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<template x-for="item in requestTypes" :key="item.id">
<option x-text="item.name"></option>
</template>
</select>
</div>
</div>
<div class="flex justify-between sm:justify-end">
<div class="sm:col-span-3">
<label for="search" class="block text-sm font-medium text-gray-700">Search</label>
<div class="mt.-1">
<input @keyup.enter="onSearchSubmitHandler" type="text" name="search" id="search" class="block w-full focus:ring-indigo-500 focus:border-indigo-500 pl-2 sm:text-sm border-gray-300 rounded-md" x-model="state.search">
</div>
</div>
</div>
</nav>
<div x-html="template"></div>
</div>
</div>
<div x-show="activeTab == tabs[1]">
<div class="p-5" x-data="list({
enabledSearch: false,
otherState: { responseType: 'All'},
otherState: { responseType: 'All', requestType: {id : '', name: 'All'} },
callback: closedCallback,
renderItem: closedRenderItem
})">
Expand All @@ -44,6 +70,20 @@
</select>
</div>
</div>
<div class="sm:block">
<div class="content-start">
<label for="filter" class="block text-sm font-medium text-gray-700">Filter by Request Type</label>
<select @change="(e) => {
state.other.requestType = requestTypes.find((obj) => { return e.target.value == obj.name})
load()
}"
id="reponseType" name="responseType" class="block mt-1 pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<template x-for="item in requestTypes" :key="item.id">
<option x-text="item.name"></option>
</template>
</select>
</div>
</div>
<div class="flex justify-between sm:justify-end">
<div class="sm:col-span-3">
<label for="search" class="block text-sm font-medium text-gray-700">Search</label>
Expand All @@ -64,10 +104,13 @@
return {
tabs : ['pending', 'closed'],
activeTab : '',
requestTypes : [{id : '', name: 'All'}],
currentSelected: -1,
data: JSON.parse('{{ . }}'),
async init(){
this.activeTab = this.tabs[0];
requestTypes = await getRequestTypes()
this.requestTypes = [...this.requestTypes, ...requestTypes]
},
onChangeTabs(tab){
this.activeTab = this.tabs[tab];
Expand Down Expand Up @@ -157,7 +200,7 @@
}

async function pendingCallback(e){
return await getItemsBy(1, 0, e.filter, e.page, e.search)
return await getItemsBy(1, 0, e.other.requestType.id, e.filter, e.page, e.search)
}

//CLOSED REQUEST
Expand Down Expand Up @@ -213,14 +256,21 @@
async function closedCallback(e){
const responses = [{name: 'All', value: 3}, {name: 'Rejected', value: 2}, {name: 'Approved', value: 1}]
selectedResponseType = responses.find((obj) => { return obj.name == e.other.responseType}).value
return await getItemsBy(1, selectedResponseType, e.filter, e.page, e.search)
return await getItemsBy(1, selectedResponseType, e.other.requestType.id, e.filter, e.page, e.search)
}

async function getItemsBy(type, status, filter, page, search){
async function getItemsBy(type, status, requestType, filter, page, search){
const offset = filter * page;
search = encodeURIComponent(search)
requestTypeParam = requestType == '' ? '' : '&requestType=' + requestType
const res = await fetch(`/api/items/type/${type}/status/${status}?filter=${filter}&offset=${offset}&search=${search}${requestTypeParam}`)
const data = await res.json()
return data
}


const res = await fetch(`/api/items/type/${type}/status/${status}?filter=${filter}&offset=${offset}&search=${search}`)
async function getRequestTypes(){
const res = await fetch(`/api/request/types`)
const data = await res.json()
return data
}
Expand Down
63 changes: 58 additions & 5 deletions src/goapp/templates/myrequests.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,44 @@
</nav>
<div x-show="activeTab == tabs[0]">
<div x-data="list({
enabledSearch: false,
otherState: { responseType: 'All', requestType: {id : '', name: 'All'} },
callback: pendingCallback,
renderItem: pendingRenderItem
})">
<nav class="bg-white flex items-center justify-between" aria-label="header">
<div class="sm:block">
<div class="content-start">
<label for="filter" class="block text-sm font-medium text-gray-700">Filter by Request Type</label>
<select @change="(e) => {
console.log(requestTypes)
state.other.requestType = requestTypes.find((obj) => { return e.target.value == obj.name})
load()
}"
id="reponseType" name="responseType" class="block mt-1 pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<template x-for="item in requestTypes" :key="item.id">
<option x-text="item.name"></option>
</template>
</select>
</div>
</div>
<div class="flex justify-between sm:justify-end">
<div class="sm:col-span-3">
<label for="search" class="block text-sm font-medium text-gray-700">Search</label>
<div class="mt.-1">
<input @keyup.enter="onSearchSubmitHandler" type="text" name="search" id="search" class="block w-full focus:ring-indigo-500 focus:border-indigo-500 pl-2 sm:text-sm border-gray-300 rounded-md" x-model="state.search">
</div>
</div>
</div>
</nav>
<div x-html="template"></div>
</div>
</div>
<div x-show="activeTab == tabs[1]">
<div class="p-5" x-data="list({
enabledSearch: false,
otherState: { responseType: 'All'},
otherState: { responseType: 'All', requestType: {id : '', name: 'All'} },
callback: closedCallback,
renderItem: closedRenderItem
})">
Expand All @@ -48,6 +76,22 @@
</select>
</div>
</div>
<div class="sm:block">
<div class="content-start">
<label for="filter" class="block text-sm font-medium text-gray-700">Filter by Request Type</label>
<select @change="(e) => {
console.log(requestTypes)
state.other.requestType = requestTypes.find((obj) => { return e.target.value == obj.name})
load()
}"
id="reponseType" name="responseType" class="block mt-1 pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<template x-for="item in requestTypes" :key="item.id">
<option x-text="item.name"></option>
</template>
</select>
</div>
</div>
<div class="flex justify-between sm:justify-end">
<div class="sm:col-span-3">
<label for="search" class="block text-sm font-medium text-gray-700">Search</label>
Expand All @@ -68,8 +112,11 @@
return {
tabs : ['pending', 'closed'],
activeTab : '',
requestTypes : [{id : '', name: 'All'}],
async init(){
this.activeTab = this.tabs[0];
requestTypes = await getRequestTypes()
this.requestTypes = [...this.requestTypes, ...requestTypes]
},
onChangeTabs(tab){
this.activeTab = this.tabs[tab];
Expand Down Expand Up @@ -117,7 +164,7 @@
}

async function pendingCallback(e){
return await getItemsBy(0, 0, e.filter, e.page, e.search)
return await getItemsBy(0, 0, e.other.requestType.id, e.filter, e.page, e.search)
}

//CLOSED REQUEST
Expand Down Expand Up @@ -173,14 +220,20 @@
async function closedCallback(e){
const responses = [{name: 'All', value: 3}, {name: 'Rejected', value: 2}, {name: 'Approved', value: 1}]
selectedResponseType = responses.find((obj) => { return obj.name == e.other.responseType}).value
return await getItemsBy(0, selectedResponseType, e.filter, e.page, e.search)
return await getItemsBy(0, selectedResponseType, e.other.requestType.id, e.filter, e.page, e.search)
}

async function getItemsBy(type, status, filter, page, search){
async function getItemsBy(type, status, requestType, filter, page, search){
const offset = filter * page;
search = encodeURIComponent(search)
requestTypeParam = requestType == '' ? '' : '&requestType=' + requestType
const res = await fetch(`/api/items/type/${type}/status/${status}?filter=${filter}&offset=${offset}&search=${search}${requestTypeParam}`)
const data = await res.json()
return data
}

const res = await fetch(`/api/items/type/${type}/status/${status}?filter=${filter}&offset=${offset}&search=${search}`)
async function getRequestTypes(){
const res = await fetch(`/api/request/types`)
const data = await res.json()
return data
}
Expand Down
9 changes: 9 additions & 0 deletions src/sqldb/Stored Procedures/PR_ApplicationModules_Select.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE PROCEDURE [dbo].[PR_ApplicationModules_Select]
AS
BEGIN
SET NOCOUNT ON
SELECT
CONVERT(varchar(36), [Id], 1) AS [Id],
[Name]
FROM [dbo].[ApplicationModules]
END
7 changes: 6 additions & 1 deletion src/sqldb/Stored Procedures/PR_Items_Select.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ CREATE PROCEDURE [dbo].[PR_Items_Select]
@Filter INT = 10,
@ItemType bit = NULL, -- NULL - ALL / 0 - REQUESTOR / 1 - APPROVER
@User varchar(100) = NULL,
@IsApproved int = 4
@IsApproved int = 4,
@RequestType varchar(100) = NULL
)
AS
BEGIN
Expand Down Expand Up @@ -47,6 +48,10 @@ BEGIN
(@IsApproved = 2 AND i.IsApproved = 0) OR -- Rejected
(@IsApproved = 3 AND i.IsApproved IS NOT NULL) -- Closed (Rejected, Approved)
-- If the value of IsApproved is 4 then select all
) AND
(
@RequestType IS NULL OR
(@RequestType IS NOT NULL AND i.ApplicationModuleId = @RequestType)
)
ORDER BY I.Created DESC
OFFSET @Offset ROWS
Expand Down
7 changes: 6 additions & 1 deletion src/sqldb/Stored Procedures/PR_Items_Total.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ CREATE PROCEDURE [dbo].[PR_Items_Total]
@Search VARCHAR(50) = '',
@ItemType bit = NULL, -- NULL - ALL / 0 - REQUESTOR / 1 - APPROVER,
@User varchar(100) = NULL,
@IsApproved int = -1 -- -1 - ALL / NULL - PENDING / 0 - REJECTED / 1 - APPROVED
@IsApproved int = 4,
@RequestType varchar(100) = NULL
)
AS
BEGIN
Expand Down Expand Up @@ -38,6 +39,10 @@ BEGIN
(@IsApproved = 2 AND i.IsApproved = 0) OR -- Rejected
(@IsApproved = 3 AND i.IsApproved IS NOT NULL) -- Closed (Rejected, Approved)
-- If the value of IsApproved is 4 then select all
) AND
(
@RequestType IS NULL OR
(@RequestType IS NOT NULL AND i.ApplicationModuleId = @RequestType)
)
) AS Items
END

0 comments on commit fd9ac35

Please sign in to comment.