Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit 24ef62a

Browse files
committed
webui: Refactor database page
Rewrite the database page to use React instead of AngularJS. While doing so this cleans up the existing code, especially on the frontend side, a lot. The looks of the page change slightly but the functionality is mostly untouched. Only resizing of the table columns is added. Note that this adds jQuery as a dependency. This is just temporarily. Please do not start using it. This also changes how NULL values are passed from the server to the frontend. This means that the memcache has to be cleared for NULL values to show up correctly in the frontend.
1 parent 294d066 commit 24ef62a

20 files changed

+693
-737
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ node_modules
5252
webui/js/dbhub.js*
5353
webui/js/auth.js
5454
webui/js/branches.js
55+
webui/js/database-view.js
5556
webui/js/db-header.js
5657
webui/js/markdown-editor.js
5758

common/postgresql.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ func DBDetails(DB *SQLiteDBinfo, loggedInUser, dbOwner, dbName, commitID string)
585585
dbQuery := `
586586
SELECT db.date_created, db.last_modified, db.watchers, db.stars, db.discussions, db.merge_requests,
587587
$3::text AS commit_id, db.commit_list->$3::text->'tree'->'entries'->0 AS db_entry, db.branches,
588-
db.release_count, db.contributors, coalesce(db.one_line_description, 'No description'),
588+
db.release_count, db.contributors, coalesce(db.one_line_description, ''),
589589
coalesce(db.full_description, 'No full description'), coalesce(db.default_table, ''), db.public,
590590
coalesce(db.source_url, ''), db.tags, coalesce(db.default_branch, ''), db.live_db,
591591
coalesce(db.live_node, '')
@@ -1329,8 +1329,8 @@ func DiscussionComments(dbOwner, dbName string, discID, comID int) (list []Discu
13291329
// FlushViewCount periodically flushes the database view count from Memcache to PostgreSQL
13301330
func FlushViewCount() {
13311331
type dbEntry struct {
1332-
Owner string
1333-
Name string
1332+
Owner string
1333+
Name string
13341334
}
13351335

13361336
// Log the start of the loop
@@ -2970,7 +2970,7 @@ func RenameDatabase(userName, dbName, newName string) error {
29702970

29712971
// Log the rename
29722972
log.Printf("Database renamed from '%s/%s' to '%s/%s'\n", SanitiseLogString(userName), SanitiseLogString(dbName),
2973-
SanitiseLogString(userName),SanitiseLogString(newName))
2973+
SanitiseLogString(userName), SanitiseLogString(newName))
29742974
return nil
29752975
}
29762976

common/sqlite.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ func ReadSQLiteDBCols(sdb *sqlite.Conn, dbTable, sortCol, sortDir string, ignore
748748
}
749749

750750
// Execute the query and retrieve the data
751-
_, _, dataRows, err = SQLiteRunQuery(sdb, QuerySourceWebUI, dbQuery, ignoreBinary, ignoreNull)
751+
_, _, dataRows, err = SQLiteRunQuery(sdb, QuerySourceAPI, dbQuery, ignoreBinary, ignoreNull)
752752
if err != nil {
753753
return dataRows, err
754754
}

common/types.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ const MinioFolderChars = 6
6464
type QuerySource int
6565

6666
const (
67-
QuerySourceWebUI QuerySource = iota
68-
QuerySourceDB4S
67+
QuerySourceDB4S QuerySource = iota
6968
QuerySourceVisualisation
7069
QuerySourceAPI
7170
QuerySourceInternal

cypress/e2e/1-webui/database.cy.js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,17 @@ describe('database page', () => {
123123

124124
// Table/view dropdown
125125
it('Table/view dropdown', () => {
126-
cy.get('[data-cy="tabledropdown"]').click()
127-
cy.get('[data-cy="row-Constituency_Turnout_Information"]').click()
128-
cy.get('[data-cy="col-Constituency_Name"]').should('contain', 'Constituency_Name')
126+
cy.get('[name="viewtable"]').parent('.react-dropdown-select').click()
127+
cy.get('[name="viewtable"]').siblings('.react-dropdown-select-dropdown').find('.react-dropdown-select-item').contains('Constituency_Turnout_Information').click()
128+
cy.get('[name="viewtable"]').should('have.value', 'Constituency_Turnout_Information')
129129
})
130130

131131
// Branch dropdown
132132
it('Branch dropdown', () => {
133-
cy.get('[data-cy="branchname"]').should('contain.text', 'main')
134-
cy.get('[data-cy="branchdropdown"]').click()
135-
cy.get('[data-cy="branch-stuff"]').click()
136-
cy.get('[data-cy="branchname"]').should('contain.text', 'stuff')
133+
cy.get('[name="viewbranch"]').should('have.value', 'main')
134+
cy.get('[name="viewbranch"]').parent('.react-dropdown-select').click()
135+
cy.get('[name="viewbranch"]').siblings('.react-dropdown-select-dropdown').find('.react-dropdown-select-item').contains('stuff').click()
136+
cy.get('[name="viewbranch"]').should('have.value', 'stuff')
137137
})
138138

139139
// New Merge Request button
@@ -207,57 +207,57 @@ describe('database page', () => {
207207

208208
// Click on column header (sort ascending)
209209
it('Click on column header - 1st time', () => {
210-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
211-
cy.get('[data-cy="datarow-Candidate_First_Pref_Votes"]').should('contain', '27')
210+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
211+
cy.get('.rdg-row').first().find('.rdg-cell').first().should('contain', '27')
212212
})
213213

214214
// Click on column header twice (sort descending)
215215
it('Click on column header - 2nd time', () => {
216-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
217-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
218-
cy.get('[data-cy="datarow-Candidate_First_Pref_Votes"]').should('contain', '10258')
216+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
217+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
218+
cy.get('.rdg-row').first().find('.rdg-cell').first().should('contain', '10258')
219219
})
220220

221221
// Click on page down button
222222
it('Page down button', () => {
223223
// Initial sort to guarantee a stable order
224-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
224+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
225225

226226
// Click the button we're testing
227227
cy.get('[data-cy="pgdnbtn"]').click()
228-
cy.get('[data-cy="datarow-Candidate_First_Pref_Votes"]').should('contain', '85')
228+
cy.get('.rdg-row').first().find('.rdg-cell').first().should('contain', '85')
229229
})
230230

231231
// Click on "go to the last page" button
232232
it('Last page button', () => {
233233
// Initial sort to guarantee a stable order
234-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
234+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
235235

236236
// Click the button we're testing
237237
cy.get('[data-cy="lastpgbtn"]').click()
238-
cy.get('[data-cy="datarow-Candidate_First_Pref_Votes"]').should('contain', '8881')
238+
cy.get('.rdg-row').first().find('.rdg-cell').first().should('contain', '8881')
239239
})
240240

241241
// Click on page up button
242242
it('Page up button', () => {
243243
// Initial sort to guarantee a stable order
244-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
244+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
245245

246246
// Click the button we're testing
247247
cy.get('[data-cy="lastpgbtn"]').click()
248248
cy.get('[data-cy="pgupbtn"]').click()
249-
cy.get('[data-cy="datarow-Candidate_First_Pref_Votes"]').should('contain', '7786')
249+
cy.get('.rdg-row').first().find('.rdg-cell').first().should('contain', '7786')
250250
})
251251

252252
// Click on "go to the first page" button
253253
it('First page button', () => {
254254
// Initial sort to guarantee a stable order
255-
cy.get('[data-cy="col-Candidate_First_Pref_Votes"]').click()
255+
cy.get('.rdg-header-row').find('span').contains('Candidate_First_Pref_Votes').click()
256256

257257
// Click the button we're testing
258258
cy.get('[data-cy="lastpgbtn"]').click()
259259
cy.get('[data-cy="firstpgbtn"]').click()
260-
cy.get('[data-cy="datarow-Candidate_First_Pref_Votes"]').should('contain', '27')
260+
cy.get('.rdg-row').first().find('.rdg-cell').first().should('contain', '27')
261261
})
262262

263263
// Readme contents

cypress/e2e/1-webui/database_sharing.cy.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
let waitTime = 250;
2+
13
describe('database sharing', () => {
24
before(() => {
35
// Seed data
@@ -16,6 +18,7 @@ describe('database sharing', () => {
1618
cy.get('[data-cy="sharedropdown-third"]').click()
1719
cy.get('[data-cy="sharerw-third"]').click({force: true})
1820
cy.get('[data-cy="savebtn"]').click()
21+
cy.wait(waitTime)
1922

2023
// Upload the test database to the user "first", plus setup some useful database sharing
2124
cy.request("/x/test/switchfirst")
@@ -35,6 +38,7 @@ describe('database sharing', () => {
3538
cy.get('[data-cy="sharedropdown-third"]').click()
3639
cy.get('[data-cy="sharero-third"]').click()
3740
cy.get('[data-cy="savebtn"]').click()
41+
cy.wait(waitTime)
3842

3943
// Upload the test database to the user "second", plus setup some useful database sharing
4044
cy.request("/x/test/switchsecond")
@@ -54,6 +58,7 @@ describe('database sharing', () => {
5458
cy.get('[data-cy="sharedropdown-first"]').click()
5559
cy.get('[data-cy="sharero-first"]').click()
5660
cy.get('[data-cy="savebtn"]').click()
61+
cy.wait(waitTime)
5762

5863
// Upload the test database to the user "third", plus setup some useful database sharing
5964
cy.request("/x/test/switchthird")
@@ -73,6 +78,7 @@ describe('database sharing', () => {
7378
cy.get('[data-cy="sharedropdown-second"]').click()
7479
cy.get('[data-cy="sharero-second"]').click()
7580
cy.get('[data-cy="savebtn"]').click()
81+
cy.wait(waitTime)
7682

7783
// Switch back to the default user
7884
cy.request("/x/test/switchdefault")

cypress/e2e/1-webui/live_execute_sql.cy.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ describe('live execute', () => {
9494
it('"Execute SQL" button', () => {
9595
// Load the data view page, and ensure the view we're about to remove is present
9696
cy.visit('/default/Join Testing with index.sqlite')
97-
cy.get('[data-cy="tabledropdown"]').click()
98-
cy.get('[data-cy="row-joinedView"]').should('exist')
97+
cy.get('[name="viewtable"]').parent('.react-dropdown-select').click()
98+
cy.get('[name="viewtable"]').siblings('.react-dropdown-select-dropdown').find('.react-dropdown-select-item').contains('joinedView').should('exist')
9999

100100
// Remove the view using the Execute SQL button
101101
cy.visit('/exec/default/Join Testing with index.sqlite')
@@ -106,8 +106,8 @@ describe('live execute', () => {
106106

107107
// Verify the view has been removed
108108
cy.visit('/default/Join Testing with index.sqlite')
109-
cy.get('[data-cy="tabledropdown"]').click()
110-
cy.get('[data-cy="row-joinedView"]').should('not.exist')
109+
cy.get('[name="viewtable"]').parent('.react-dropdown-select').click()
110+
cy.get('[name="viewtable"]').siblings('.react-dropdown-select-dropdown').find('.react-dropdown-select-item').contains('joinedView').should('not.exist')
111111
})
112112

113113
// "Delete" button
@@ -137,4 +137,4 @@ describe('live execute', () => {
137137
// Switch back to the default user
138138
cy.request('/x/test/switchdefault')
139139
})
140-
})
140+
})

cypress/e2e/1-webui/settings.cy.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
let waitTime = 250;
2+
13
describe('settings', () => {
24
before(() => {
35
// Seed data
@@ -27,7 +29,7 @@ describe('settings', () => {
2729
// One line description
2830
it('one line description', () => {
2931
cy.visit('settings/default/Assembly%20Election%202017.sqlite')
30-
cy.get('[data-cy="onelinedescinput"]').should('have.value', 'No description')
32+
cy.get('[data-cy="onelinedescinput"]').should('have.value', '')
3133
cy.get('[data-cy="onelinedescinput"]').type('{selectall}{backspace}Some new description')
3234
cy.get('[data-cy="savebtn"]').click()
3335
cy.get('[data-cy="settingslink"]').click()
@@ -111,6 +113,7 @@ describe('settings', () => {
111113
// Set the database to Private
112114
cy.get('[data-cy="private"]').click()
113115
cy.get('[data-cy="savebtn"]').click()
116+
cy.wait(waitTime)
114117

115118
// Ensure the database cannot be seen by the other users
116119
cy.request("/x/test/switchfirst")

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
"babel-preset-react-app": "^10.0.1",
55
"cypress": "^12.3.0",
66
"react": "^18.2.0",
7+
"react-data-grid": "^7.0.0-beta.28",
78
"react-dom": "^18.2.0",
9+
"react-dropdown-select": "^4.9.3",
810
"react-modal-image": "^2.6.0",
911
"react-tabs": "^6.0.0",
1012
"yarn": "^1.22.19"

webui/js/app.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ModalImage from "react-modal-image";
55

66
import Auth from "./auth";
77
import BranchesTable from "./branches";
8+
import DatabaseView from "./database-view";
89
import DbHeader from "./db-header";
910
import MarkdownEditor from "./markdown-editor";
1011

@@ -32,6 +33,14 @@ import MarkdownEditor from "./markdown-editor";
3233
}
3334
}
3435

36+
{
37+
const rootNode = document.getElementById("database-view");
38+
if (rootNode) {
39+
const root = ReactDOM.createRoot(rootNode);
40+
root.render(React.createElement(DatabaseView));
41+
}
42+
}
43+
3544
{
3645
document.querySelectorAll(".markdown-editor").forEach((rootNode) => {
3746
const editorId = rootNode.dataset.id;

0 commit comments

Comments
 (0)