Skip to content

Commit 87f7968

Browse files
committed
Actions for multiple rows and styling
1 parent 4246898 commit 87f7968

File tree

7 files changed

+81
-96
lines changed

7 files changed

+81
-96
lines changed

admin_ui/src/components/CallbackButton.vue

Lines changed: 0 additions & 55 deletions
This file was deleted.

admin_ui/src/interfaces.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ export interface DeleteRow {
88
rowID: number
99
}
1010

11-
export interface ExecuteCallback {
11+
export interface ExecuteAction {
1212
tableName: string
13-
rowID: number
13+
rowIDs: number
14+
actionId: number
1415
}
1516

1617
export interface UpdateRow {
@@ -149,3 +150,8 @@ export interface FormConfig {
149150
slug: string
150151
description: string
151152
}
153+
154+
export interface Action {
155+
actionID: number
156+
actionName: string
157+
}

admin_ui/src/store.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export default new Vuex.Store({
3333
tableNames: [],
3434
formConfigs: [] as i.FormConfig[],
3535
user: undefined,
36-
loadingStatus: false
36+
loadingStatus: false,
37+
actions: [] as i.Action[]
3738
},
3839
mutations: {
3940
updateTableNames(state, value) {
@@ -66,6 +67,9 @@ export default new Vuex.Store({
6667
updateSortBy(state, config: i.SortByConfig) {
6768
state.sortBy = config
6869
},
70+
updateActions(state, actions) {
71+
state.actions = actions
72+
},
6973
reset(state) {
7074
state.sortBy = null
7175
state.filterParams = {}
@@ -229,12 +233,19 @@ export default new Vuex.Store({
229233
)
230234
return response
231235
},
232-
async executeCallback(context, config: i.ExecuteCallback) {
236+
async fetchActions(context, tableName: string) {
237+
const response = await axios.get(
238+
`${BASE_URL}tables/${tableName}/actions`
239+
)
240+
context.commit("updateActions", response.data)
241+
return response
242+
},
243+
async executeAction(context, config: i.ExecuteAction) {
233244
const response = await axios.post(
234-
`${BASE_URL}tables/${config.tableName}/callback/execute`,
245+
`${BASE_URL}tables/${config.tableName}/actions/${config.actionId}/execute`,
235246
{
236247
table_name: config.tableName,
237-
row_id: config.rowID
248+
row_ids: config.rowIDs
238249
}
239250
)
240251
return response

admin_ui/src/views/RowListing.vue

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@
1111
/>
1212
</div>
1313
<div class="buttons">
14+
<a class="button" v-on:click.prevent="showActionDropDown = !showActionDropDown">
15+
<font-awesome-icon icon="angle-right" v-if="!showActionDropDown"/>
16+
<font-awesome-icon icon="angle-down" v-if="showActionDropDown"/>
17+
Actions
18+
<a class="actions-dropdown">
19+
<DropDownMenu v-if="showActionDropDown">
20+
<li v-for="action in allActions">
21+
<a href="#" class="button" v-on:click="executeAction(action.action_id)">
22+
<span>{{ action.action_name }}</span>
23+
</a>
24+
</li>
25+
</DropDownMenu>
26+
</a>
27+
</a>
1428
<a
1529
class="button"
1630
href="#"
@@ -27,7 +41,8 @@
2741
v-if="selectedRows.length > 0"
2842
v-on:triggered="deleteRows"
2943
/>
30-
44+
45+
3146
<router-link
3247
:to="{
3348
name: 'addRow',
@@ -40,6 +55,7 @@
4055
<span>{{ $t("Add Row") }}</span>
4156
</router-link>
4257

58+
4359
<a
4460
class="button"
4561
href="#"
@@ -299,17 +315,6 @@
299315
"
300316
/>
301317
</li>
302-
<li>
303-
<CallbackButton
304-
:includeTitle="true"
305-
class=""
306-
v-on:triggered="
307-
executeCallback(
308-
row[pkName]
309-
)
310-
"
311-
/>
312-
</li>
313318
</DropDownMenu>
314319
</span>
315320
</td>
@@ -381,7 +386,6 @@ import BulkUpdateModal from "../components/BulkUpdateModal.vue"
381386
import BulkDeleteButton from "../components/BulkDeleteButton.vue"
382387
import CSVButton from "../components/CSVButton.vue"
383388
import DeleteButton from "../components/DeleteButton.vue"
384-
import CallbackButton from "../components/CallbackButton.vue"
385389
import DropDownMenu from "../components/DropDownMenu.vue"
386390
import ChangePageSize from "../components/ChangePageSize.vue"
387391
import MediaViewer from "../components/MediaViewer.vue"
@@ -409,7 +413,8 @@ export default Vue.extend({
409413
showUpdateModal: false,
410414
visibleDropdown: null,
411415
showMediaViewer: false,
412-
mediaViewerConfig: null as MediaViewerConfig
416+
mediaViewerConfig: null as MediaViewerConfig,
417+
showActionDropDown: false,
413418
}
414419
},
415420
components: {
@@ -420,7 +425,6 @@ export default Vue.extend({
420425
ChangePageSize,
421426
CSVButton,
422427
DeleteButton,
423-
CallbackButton,
424428
DropDownMenu,
425429
MediaViewer,
426430
Pagination,
@@ -448,6 +452,9 @@ export default Vue.extend({
448452
schema() {
449453
return this.$store.state.schema
450454
},
455+
allActions() {
456+
return this.$store.state.actions
457+
},
451458
rowCount() {
452459
return this.$store.state.rowCount
453460
},
@@ -579,24 +586,22 @@ export default Vue.extend({
579586
this.showSuccess("Successfully deleted row")
580587
}
581588
},
582-
async executeCallback(rowID) {
583-
if (confirm(`Are you sure you want to run callback for this row?`)) {
584-
console.log("Requesting to run callback!")
589+
async executeAction(action_id) {
590+
if (confirm(`Are you sure you want to run action for this row?`)) {
585591
try {
586-
let response = await this.$store.dispatch("executeCallback", {
592+
let response = await this.$store.dispatch("executeAction", {
587593
tableName: this.tableName,
588-
rowID
594+
rowIDs: this.selectedRows,
595+
actionId: action_id
589596
})
590-
this.showSuccess(`Successfully ran callback and got response: ${response.data}`, 10000)
597+
this.showSuccess(`Successfully ran action and got response: ${response.data}`, 10000)
591598
} catch (error) {
592599
if (error.response.status == 404) {
593-
console.log(error.response.status)
594600
this.$store.commit("updateApiResponseMessage", {
595-
contents: "This table is not configured for callback action",
601+
contents: "This table is not configured for any actions",
596602
type: "error"
597603
})
598604
} else {
599-
console.log(error.response.status)
600605
this.$store.commit("updateApiResponseMessage", {
601606
contents: "Something went wrong",
602607
type: "error"
@@ -623,6 +628,9 @@ export default Vue.extend({
623628
},
624629
async fetchSchema() {
625630
await this.$store.dispatch("fetchSchema", this.tableName)
631+
},
632+
async fetchActions() {
633+
await this.$store.dispatch("fetchActions", this.tableName)
626634
}
627635
},
628636
watch: {
@@ -650,7 +658,7 @@ export default Vue.extend({
650658
this.$router.currentRoute.query
651659
)
652660
653-
await Promise.all([this.fetchRows(), this.fetchSchema()])
661+
await Promise.all([this.fetchRows(), this.fetchSchema(), this.fetchActions()])
654662
}
655663
})
656664
</script>
@@ -727,6 +735,10 @@ div.wrapper {
727735
&:first-child {
728736
margin-left: 0;
729737
}
738+
739+
a.actions-dropdown {
740+
position: relative;
741+
}
730742
}
731743
}
732744
}

admin_ui/vue.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ module.exports = {
77
port: 3000,
88
proxy: {
99
'^/api': {
10-
target: 'http://localhost:8000'
10+
target: 'http://127.0.0.1:8000'
1111
},
1212
'^/public': {
13-
target: 'http://localhost:8000'
13+
target: 'http://127.0.0.1:8000'
1414
}
1515
},
1616
}

piccolo_admin/endpoints.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ async def manager_only(
152152
validators=Validators(post_single=manager_only)
153153
)
154154
)
155+
:param custom_actions: Optional list of custom actions handler function
155156
156157
"""
157158

@@ -164,7 +165,7 @@ async def manager_only(
164165
hooks: t.Optional[t.List[Hook]] = None
165166
media_storage: t.Optional[t.Sequence[MediaStorage]] = None
166167
validators: t.Optional[Validators] = None
167-
custom_callback: t.Optional[t.Callable] = None
168+
custom_actions: t.Optional[t.List[t.Callable]] = None
168169

169170
def __post_init__(self):
170171
if self.visible_columns and self.exclude_visible_columns:
@@ -464,7 +465,7 @@ def __init__(
464465
"tags": [f"{table_class._meta.tablename.capitalize()}"]
465466
},
466467
),
467-
callback=table_config.custom_callback,
468+
actions=table_config.custom_actions,
468469
)
469470

470471
private_app.add_api_route(

piccolo_admin/example.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from piccolo_api.session_auth.tables import SessionsBase
4545
from pydantic import BaseModel, validator
4646
from starlette.responses import JSONResponse
47+
from piccolo_api.fastapi.endpoints import TableRowDataSchema
4748

4849
from piccolo_admin.endpoints import FormConfig, TableConfig, create_admin
4950
from piccolo_admin.example_data import (
@@ -164,6 +165,7 @@ class Genre(int, enum.Enum):
164165
romance = 7
165166
musical = 8
166167

168+
id = BigInt
167169
name = Varchar(length=300)
168170
rating = Real(help_text="The rating on IMDB.")
169171
duration = Interval()
@@ -259,11 +261,6 @@ async def booking_endpoint(request, data):
259261

260262
return "Booking complete"
261263

262-
async def my_callback_fn(**kwargs) -> JSONResponse:
263-
request_data = kwargs['request_params']
264-
table_name = request_data['table_name']
265-
row_id = request_data['row_id']
266-
return JSONResponse(f"My API received the row_id: {row_id} from table: {table_name}")
267264

268265
TABLE_CLASSES: t.Tuple[t.Type[Table], ...] = (
269266
Director,
@@ -275,6 +272,19 @@ async def my_callback_fn(**kwargs) -> JSONResponse:
275272
)
276273

277274

275+
# CUSTOM ACTIONS
276+
async def my_custom_action(data: TableRowDataSchema) -> JSONResponse:
277+
row_ids = data.row_ids
278+
279+
# Use the selected row ids to fetch rows from db
280+
movies = await Movie.select().where(Movie.id == row_ids[0])
281+
282+
# Return a JSONResponse which can be displayed back to the frontend
283+
return JSONResponse(f"My API received the row_id: {row_ids}")
284+
285+
async def custom_action_2(data) -> JSONResponse:
286+
return JSONResponse("This is the second action")
287+
278288
movie_config = TableConfig(
279289
table_class=Movie,
280290
visible_columns=[
@@ -307,7 +317,7 @@ async def my_callback_fn(**kwargs) -> JSONResponse:
307317
media_path=os.path.join(MEDIA_ROOT, "movie_screenshots"),
308318
),
309319
),
310-
custom_callback=my_callback_fn
320+
custom_actions=[my_custom_action, custom_action_2]
311321
)
312322

313323
director_config = TableConfig(

0 commit comments

Comments
 (0)