From c8311e0642f76b8d1ab278a0aeffe773e5321af1 Mon Sep 17 00:00:00 2001 From: gergicsmilan Date: Mon, 27 May 2019 11:14:10 +0200 Subject: [PATCH 01/34] clear boards function implemented --- static/js/dom.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/static/js/dom.js b/static/js/dom.js index 0092afb7..8b324d76 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -1,5 +1,5 @@ // It uses data_handler.js to visualize elements -import { dataHandler } from "./data_handler.js"; +import {dataHandler} from "./data_handler.js"; export let dom = { _appendToElement: function (elementToExtend, textToAppend, prepend = false) { @@ -20,9 +20,14 @@ export let dom = { init: function () { // This function should run once, when the page is loaded. }, + clearBoards: function () { + let boardsDiv = document.querySelector("#boards"); + boardsDiv.textContent = ""; + }, loadBoards: function () { // retrieves boards and makes showBoards called - dataHandler.getBoards(function(boards){ + dataHandler.getBoards(boards => { + this.clearBoards(); dom.showBoards(boards); }); }, @@ -32,7 +37,7 @@ export let dom = { let boardList = ''; - for(let board of boards){ + for (let board of boards) { boardList += `
  • ${board.title}
  • `; From 14b00cefc7f59f4300f7b3c47ed995aeb61c6f73 Mon Sep 17 00:00:00 2001 From: gergicsmilan Date: Mon, 27 May 2019 14:06:13 +0200 Subject: [PATCH 02/34] =?UTF-8?q?implemented=20connection.py=20and=20(Fru:?= =?UTF-8?q?=20sql=20install=C3=A9son=20f=C3=A1jl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connection.py | 52 +++++++++++++++++++++ sample_data/proman-sample-data.sql | 75 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 connection.py create mode 100644 sample_data/proman-sample-data.sql diff --git a/connection.py b/connection.py new file mode 100644 index 00000000..64723248 --- /dev/null +++ b/connection.py @@ -0,0 +1,52 @@ +# Creates a decorator to handle the database connection/cursor opening/closing. +# Creates the cursor with RealDictCursor, thus it returns real dictionaries, where the column names are the keys. +import os +import psycopg2 +import psycopg2.extras + + +def get_connection_string(): + # setup connection string + # to do this, please define these environment variables first + user_name = os.environ.get('PSQL_USER_NAME') + password = os.environ.get('PSQL_PASSWORD') + host = os.environ.get('PSQL_HOST') + database_name = os.environ.get('PSQL_DB_NAME') + + env_variables_defined = user_name and password and host and database_name + + if env_variables_defined: + + # this string describes all info for psycopg2 to connect to the database + return 'postgresql://{user_name}:{password}@{host}/{database_name}'.format( + user_name=user_name, + password=password, + host=host, + database_name=database_name + ) + else: + raise KeyError('Some necessary environment variable(s) are not defined') + + +def open_database(): + try: + connection_string = get_connection_string() + connection = psycopg2.connect(connection_string) + connection.autocommit = True + except psycopg2.DatabaseError as exception: + print('Database connection problem') + raise exception + return connection + + +def connection_handler(function): + def wrapper(*args, **kwargs): + connection = open_database() + # we set the cursor_factory parameter to return with a RealDictCursor cursor (cursor which provide dictionaries) + dict_cur = connection.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + ret_value = function(dict_cur, *args, **kwargs) + dict_cur.close() + connection.close() + return ret_value + + return wrapper diff --git a/sample_data/proman-sample-data.sql b/sample_data/proman-sample-data.sql new file mode 100644 index 00000000..cab7ea8a --- /dev/null +++ b/sample_data/proman-sample-data.sql @@ -0,0 +1,75 @@ +ALTER TABLE IF EXISTS ONLY public.board DROP CONSTRAINT IF EXISTS pk_board_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.card DROP CONSTRAINT IF EXISTS pk_card_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.status DROP CONSTRAINT IF EXISTS pk_status_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.card DROP CONSTRAINT IF EXISTS fk_board_id CASCADE; +ALTER TABLE IF EXISTS ONLY public.card DROP CONSTRAINT IF EXISTS fk_status_id CASCADE; + + +DROP TABLE IF EXISTS public.board; +DROP SEQUENCE IF EXISTS public.board_id_seq; +CREATE TABLE board( + id serial NOT NULL, + title text +); + + +DROP TABLE IF EXISTS public.card; +DROP SEQUENCE IF EXISTS public.card_id_seq; +CREATE TABLE card( + id serial NOT NULL, + board_id integer NOT NULL, + title text, + status_id integer NOT NULL, + card_order integer +); + + +DROP TABLE IF EXISTS public.status; +DROP SEQUENCE IF EXISTS public.status_id_seq; +CREATE TABLE status( + id serial NOT NULL , + title text +); + + +ALTER TABLE ONLY board + ADD CONSTRAINT pk_board_id PRIMARY KEY (id); + +ALTER TABLE ONLY card + ADD CONSTRAINT pk_card_id PRIMARY KEY (id); + +ALTER TABLE ONLY status + ADD CONSTRAINT pk_status_id PRIMARY KEY (id); + +ALTER TABLE ONLY card + ADD CONSTRAINT fk_board_id FOREIGN KEY (board_id) REFERENCES board(id); + +ALTER TABLE ONLY card + ADD CONSTRAINT fk_status_id FOREIGN KEY (status_id) REFERENCES status(id); + + +INSERT INTO board VALUES (1, 'board 1'); +INSERT INTO board VALUES (2, 'board 2'); +SELECT pg_catalog.setval('board_id_seq', 3, true); + +INSERT INTO status VALUES (0, 'new'); +INSERT INTO status VALUES (1, 'in progress'); +INSERT INTO status VALUES (2, 'testing'); +INSERT INTO status VALUES (3, 'done'); +SELECT pg_catalog.setval('status_id_seq', 4, true); + +INSERT INTO card VALUES (1, 1, 'new card1', 0, 0); +INSERT INTO card VALUES (2, 1, 'new card2', 0, 1); +INSERT INTO card VALUES (3, 1, 'in progress card', 1, 0); +INSERT INTO card VALUES (4, 1, 'planning', 2, 0); +INSERT INTO card VALUES (5, 1, 'done card', 3, 0); +INSERT INTO card VALUES (6, 1, 'done card', 3, 1); +INSERT INTO card VALUES (7, 2, 'new card1', 0, 0); +INSERT INTO card VALUES (8, 2, 'new card2', 0, 1); +INSERT INTO card VALUES (9, 2, 'in progress card', 1, 0); +INSERT INTO card VALUES (10, 2, 'planning', 2, 0); +INSERT INTO card VALUES (11, 2, 'done card', 3, 0); +INSERT INTO card VALUES (12, 2, 'done card', 3, 1); +SELECT pg_catalog.setval('card_id_seq', 13, true); + + From b4c0b23c0b6376148580c59ccbaae9c651f30724 Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Mon, 27 May 2019 15:40:38 +0200 Subject: [PATCH 03/34] sql requests implemented for board, card and status tables --- data_handler.py | 2 +- persistence.py | 64 +++++++++++++++++-------------------------------- 2 files changed, 23 insertions(+), 43 deletions(-) diff --git a/data_handler.py b/data_handler.py index 0d8d9086..d7bf5fa7 100644 --- a/data_handler.py +++ b/data_handler.py @@ -16,7 +16,7 @@ def get_boards(): Gather all boards :return: """ - return persistence.get_boards(force=True) + return persistence.get_boards() def get_cards_for_board(board_id): diff --git a/persistence.py b/persistence.py index 1e1f1e3d..8e05f49c 100644 --- a/persistence.py +++ b/persistence.py @@ -1,51 +1,31 @@ -import csv +import connection -STATUSES_FILE = './data/statuses.csv' -BOARDS_FILE = './data/boards.csv' -CARDS_FILE = './data/cards.csv' -_cache = {} # We store cached data in this dict to avoid multiple file readings +@connection.connection_handler +def get_statuses(cursor): + cursor.execute(""" + SELECT id, title FROM status; + """) + statuses = cursor.fetchall() + return statuses -def _read_csv(file_name): - """ - Reads content of a .csv file - :param file_name: relative path to data file - :return: OrderedDict - """ - with open(file_name) as boards: - rows = csv.DictReader(boards, delimiter=',', quotechar='"') - formatted_data = [] - for row in rows: - formatted_data.append(dict(row)) - return formatted_data +@connection.connection_handler +def get_boards(cursor): + cursor.execute(""" + SELECT id, title FROM board; + """) -def _get_data(data_type, file, force): - """ - Reads defined type of data from file or cache - :param data_type: key where the data is stored in cache - :param file: relative path to data file - :param force: if set to True, cache will be ignored - :return: OrderedDict - """ - if force or data_type not in _cache: - _cache[data_type] = _read_csv(file) - return _cache[data_type] + boards = cursor.fetchall() + return boards -def clear_cache(): - for k in list(_cache.keys()): - _cache.pop(k) +@connection.connection_handler +def get_cards(cursor): + cursor.execute(""" + SELECT id, board_id, title, status_id, card_order FROM card; + """) - -def get_statuses(force=False): - return _get_data('statuses', STATUSES_FILE, force) - - -def get_boards(force=False): - return _get_data('boards', BOARDS_FILE, force) - - -def get_cards(force=False): - return _get_data('cards', CARDS_FILE, force) + cards = cursor.fetchall() + return cards From 730c2f427aa9e38388629f5c3c0d8a9526667239 Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Tue, 28 May 2019 14:58:03 +0200 Subject: [PATCH 04/34] test --- data_handler.py | 9 +-------- persistence.py | 10 ++++++++++ static/js/data_handler.js | 5 +++++ static/js/dom.js | 10 ++++++++++ static/js/main.js | 1 + templates/index.html | 5 ++++- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/data_handler.py b/data_handler.py index d7bf5fa7..b7a19b49 100644 --- a/data_handler.py +++ b/data_handler.py @@ -20,11 +20,4 @@ def get_boards(): def get_cards_for_board(board_id): - persistence.clear_cache() - all_cards = persistence.get_cards() - matching_cards = [] - for card in all_cards: - if card['board_id'] == str(board_id): - card['status_id'] = get_card_status(card['status_id']) # Set textual status for the card - matching_cards.append(card) - return matching_cards + return persistence.get_cards_for_board(board_id) diff --git a/persistence.py b/persistence.py index 8e05f49c..d43f26a5 100644 --- a/persistence.py +++ b/persistence.py @@ -29,3 +29,13 @@ def get_cards(cursor): cards = cursor.fetchall() return cards + +@connection.connection_handler +def get_cards_for_board(cursor, board_id): + cursor.execute("""SELECT id, board_id, title, status_id, card_order FROM card + WHERE board_id = %(board_id)s; + """, + {'board_id': board_id}) + + cards = cursor.fetchall() + return cards \ No newline at end of file diff --git a/static/js/data_handler.js b/static/js/data_handler.js index 66df0ba8..033074de 100644 --- a/static/js/data_handler.js +++ b/static/js/data_handler.js @@ -43,6 +43,11 @@ export let dataHandler = { }, getCardsByBoardId: function (boardId, callback) { // the cards are retrieved and then the callback function is called with the cards + + this._api_get('/get-cards/', (response) => { + this._data = response; + callback(response); + }); }, getCard: function (cardId, callback) { // the card is retrieved and then the callback function is called with the card diff --git a/static/js/dom.js b/static/js/dom.js index 8b324d76..e572846f 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -29,6 +29,9 @@ export let dom = { dataHandler.getBoards(boards => { this.clearBoards(); dom.showBoards(boards); + for(let board of boards) { + dom.loadCards(parseInt(board.id)); + } }); }, showBoards: function (boards) { @@ -53,10 +56,17 @@ export let dom = { }, loadCards: function (boardId) { // retrieves cards and makes showCards called + + console.log(boardId); + dataHandler.getCardsByBoardId(boardId, dom.showCards); + }, showCards: function (cards) { // shows the cards of a board // it adds necessary event listeners also + for (let card of cards) { + console.log(card); + } }, // here comes more features }; diff --git a/static/js/main.js b/static/js/main.js index f9298ae2..afff80c3 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -7,6 +7,7 @@ function init() { // loads the boards to the screen dom.loadBoards(); + } init(); diff --git a/templates/index.html b/templates/index.html index 6f42f26e..48f40fe0 100644 --- a/templates/index.html +++ b/templates/index.html @@ -16,6 +16,9 @@

    ProMan

    -
    Boards are loading...
    +
    Boards are loading... +
    + +
    \ No newline at end of file From 0311eead3b11413a7668ab0eaa52b23f739895ef Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Tue, 28 May 2019 15:10:48 +0200 Subject: [PATCH 05/34] test2 --- main.py | 4 ++-- static/js/data_handler.js | 2 +- static/js/dom.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 9e25a1ac..aeb5c5cf 100644 --- a/main.py +++ b/main.py @@ -23,9 +23,9 @@ def get_boards(): return data_handler.get_boards() -@app.route("/get-cards/") +@app.route("/get-cards/") @json_response -def get_cards_for_board(board_id: int): +def get_cards_for_board(board_id): """ All cards that belongs to a board :param board_id: id of the parent board diff --git a/static/js/data_handler.js b/static/js/data_handler.js index 033074de..e75814cf 100644 --- a/static/js/data_handler.js +++ b/static/js/data_handler.js @@ -44,7 +44,7 @@ export let dataHandler = { getCardsByBoardId: function (boardId, callback) { // the cards are retrieved and then the callback function is called with the cards - this._api_get('/get-cards/', (response) => { + this._api_get(`/get-cards/${boardId}`, (response) => { this._data = response; callback(response); }); diff --git a/static/js/dom.js b/static/js/dom.js index e572846f..a69582ca 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -64,7 +64,7 @@ export let dom = { showCards: function (cards) { // shows the cards of a board // it adds necessary event listeners also - for (let card of cards) { + for(let card of cards) { console.log(card); } }, From dd8c17764ac7a09f3aeec6b9f3019bea82db5f64 Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Wed, 29 May 2019 14:08:40 +0200 Subject: [PATCH 06/34] listing board and cards dinamically from sql database is implemented --- data_handler.py | 5 ++ main.py | 10 +++ persistence.py | 9 +++ static/css/design.css | 129 ++++++++++++++++++++++++++++++++++++++ static/css/main.css | 3 - static/js/data_handler.js | 10 ++- static/js/dom.js | 55 ++++++++-------- templates/index.html | 40 ++++++------ templates/template.html | 36 +++++++++++ 9 files changed, 245 insertions(+), 52 deletions(-) create mode 100644 static/css/design.css delete mode 100644 static/css/main.css create mode 100644 templates/template.html diff --git a/data_handler.py b/data_handler.py index b7a19b49..84e9db53 100644 --- a/data_handler.py +++ b/data_handler.py @@ -21,3 +21,8 @@ def get_boards(): def get_cards_for_board(board_id): return persistence.get_cards_for_board(board_id) + + +def get_cards(): + + return persistence.get_cards() \ No newline at end of file diff --git a/main.py b/main.py index aeb5c5cf..c513efb9 100644 --- a/main.py +++ b/main.py @@ -32,6 +32,16 @@ def get_cards_for_board(board_id): """ return data_handler.get_cards_for_board(board_id) +@app.route("/get-cards") +@json_response +def get_cards(): + """ + All cards that belongs to a board + :param board_id: id of the parent board + """ + return data_handler.get_cards() + + def main(): app.run(debug=True) diff --git a/persistence.py b/persistence.py index d43f26a5..6c128f7f 100644 --- a/persistence.py +++ b/persistence.py @@ -38,4 +38,13 @@ def get_cards_for_board(cursor, board_id): {'board_id': board_id}) cards = cursor.fetchall() + return cards + +@connection.connection_handler +def get_cards(cursor): + cursor.execute("""SELECT id, board_id, title, status_id, card_order FROM card; + """) + + cards = cursor.fetchall() + print(cards) return cards \ No newline at end of file diff --git a/static/css/design.css b/static/css/design.css new file mode 100644 index 00000000..a8b8f2f2 --- /dev/null +++ b/static/css/design.css @@ -0,0 +1,129 @@ +:root{ + --border-radius: 3px; + --status-1: #590000; + --status-2: #594300; + --status-3: #525900; + --status-4: #085900; +} + +body{ + min-width: 620px; + background: #ddd url(http://cdn.backgroundhost.com/backgrounds/subtlepatterns/diagonal-noise.png); + font-family: sans-serif; +} + +h1, .board-title, .board-column-title{ + font-weight: 100; +} + +h1{ + text-align: center; + font-size: 4em; + letter-spacing: 5px; + transform: scale(1.2, 1); +} + +button{ + background: #222; + color: #fff; + border: none; + font-size: 14px; + font-family: sans-serif; + padding: 4px 10px; +} + +.board-container{ + max-width: 960px; + margin: 0 auto; +} + +section.board{ + margin: 20px; + border: aliceblue; + background: #ffffff90; + border-radius: 3px; +} + +.board-header{ + height: 50px; + background: #fff; + border-radius: var(--border-radius) var(--border-radius) 0 0; +} + +.board-title{ + margin: 13px; + display: inline-block; + font-size: 20px; +} +.board-title, .board-add, .board-toggle{ + display: inline-block; +} + +.board-toggle{ + float: right; + margin: 13px; +} + +.board-columns{ + display: flex; + flex-wrap: nowrap; +} + +.board-column{ + padding: 10px; + flex: 1; +} + +.board-column-content{ + min-height: 49px; +} + +.board-column-content:empty{ + /*This only works if the tag is really empty and there is not even whitespace inside*/ + border: 4px solid #cdcdcd; + margin-top: 4px; + border-radius: 10px; + background: #eee; +} + +.board-column-title{ + text-align: center; +} + +.card{ + position: relative; + background: #222; + color: #fff; + border-radius: var(--border-radius); + margin: 4px 0; + padding: 4px; +} + +.board-column:nth-of-type(1) .card{ + background: var(--status-1); +} + +.board-column:nth-of-type(2) .card{ + background: var(--status-2); +} + +.board-column:nth-of-type(3) .card{ + background: var(--status-3); +} + +.board-column:nth-of-type(4) .card{ + background: var(--status-4); +} + +.card-remove{ + display: block; + position: absolute; + top: 4px; + right: 4px; + font-size: 12px; + cursor: pointer; +} + +.card-title{ + padding-right: 16px; +} diff --git a/static/css/main.css b/static/css/main.css deleted file mode 100644 index d8f3b84f..00000000 --- a/static/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -#boards { - background-color: green; -} \ No newline at end of file diff --git a/static/js/data_handler.js b/static/js/data_handler.js index e75814cf..0e07d6e4 100644 --- a/static/js/data_handler.js +++ b/static/js/data_handler.js @@ -46,7 +46,7 @@ export let dataHandler = { this._api_get(`/get-cards/${boardId}`, (response) => { this._data = response; - callback(response); + callback(response, boardId); }); }, getCard: function (cardId, callback) { @@ -57,6 +57,12 @@ export let dataHandler = { }, createNewCard: function (cardTitle, boardId, statusId, callback) { // creates new card, saves it and calls the callback function with its data - } + }, // here comes more features + getCards: function (callback) { + this._api_get(`/get-cards`, (response) => { + this._data = response; + callback(response); + }); + } }; diff --git a/static/js/dom.js b/static/js/dom.js index a69582ca..b63268f5 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -20,53 +20,50 @@ export let dom = { init: function () { // This function should run once, when the page is loaded. }, - clearBoards: function () { - let boardsDiv = document.querySelector("#boards"); - boardsDiv.textContent = ""; - }, loadBoards: function () { // retrieves boards and makes showBoards called dataHandler.getBoards(boards => { - this.clearBoards(); dom.showBoards(boards); - for(let board of boards) { - dom.loadCards(parseInt(board.id)); - } }); }, showBoards: function (boards) { // shows boards appending them to #boards div // it adds necessary event listeners also - let boardList = ''; - + let boardContainer = document.querySelector('.board-container'); for (let board of boards) { - boardList += ` -
  • ${board.title}
  • - `; + const template = document.querySelector('#board-template'); + const clone = document.importNode(template.content, true); + let columns = clone.querySelectorAll('.board-column-content'); + for (let column of columns) { + column.setAttribute("data-board-id", board.id); + } + clone.querySelector('.board').setAttribute("id", board.id); + clone.querySelector('.board-title').innerHTML = board.title; + boardContainer.appendChild(clone); + dom.loadCards(parseInt(board.id)); } - - const outerHtml = ` -
      - ${boardList} -
    - `; - - this._appendToElement(document.querySelector('#boards'), outerHtml); }, loadCards: function (boardId) { // retrieves cards and makes showCards called - - console.log(boardId); dataHandler.getCardsByBoardId(boardId, dom.showCards); }, - showCards: function (cards) { - // shows the cards of a board - // it adds necessary event listeners also - for(let card of cards) { - console.log(card); + showCards: function (cards, boardId) { + + let cardContainer = document.querySelectorAll('.board-column-content'); + + for (let card of cards) { + if (parseInt(boardId) === parseInt(card.board_id)) { + for (let column of cardContainer) { + if (parseInt(card.status_id) === parseInt(column.id) && parseInt(card.board_id) === parseInt(column.dataset.boardId)) { + const template = document.querySelector('#cards-template'); + const clone = document.importNode(template.content, true); + clone.querySelector('.card-title').textContent = card.title; + column.appendChild(clone); + } + } + } } }, - // here comes more features }; diff --git a/templates/index.html b/templates/index.html index 48f40fe0..d56243d4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,24 +1,28 @@ - - - - - ProMan + + + + + ProMan - - - - + + + + - - - - -

    ProMan

    -
    Boards are loading... -
    + -
    - + + + +

    ProMan

    +
    + +
    +{% include 'template.html' %} + \ No newline at end of file diff --git a/templates/template.html b/templates/template.html new file mode 100644 index 00000000..ee69ecfa --- /dev/null +++ b/templates/template.html @@ -0,0 +1,36 @@ +{#board template#} + + + +{#cards template#} + \ No newline at end of file From 6945d9eaed1b6febfd5dc661b256ab32e374255b Mon Sep 17 00:00:00 2001 From: gergicsmilan Date: Wed, 29 May 2019 14:20:00 +0200 Subject: [PATCH 07/34] my chg --- static/css/design.css | 129 ++++++++++++++++++++++++++++++++++++++++ static/css/main.css | 3 - templates/index.html | 40 +++++++------ templates/template.html | 36 +++++++++++ 4 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 static/css/design.css delete mode 100644 static/css/main.css create mode 100644 templates/template.html diff --git a/static/css/design.css b/static/css/design.css new file mode 100644 index 00000000..a8b8f2f2 --- /dev/null +++ b/static/css/design.css @@ -0,0 +1,129 @@ +:root{ + --border-radius: 3px; + --status-1: #590000; + --status-2: #594300; + --status-3: #525900; + --status-4: #085900; +} + +body{ + min-width: 620px; + background: #ddd url(http://cdn.backgroundhost.com/backgrounds/subtlepatterns/diagonal-noise.png); + font-family: sans-serif; +} + +h1, .board-title, .board-column-title{ + font-weight: 100; +} + +h1{ + text-align: center; + font-size: 4em; + letter-spacing: 5px; + transform: scale(1.2, 1); +} + +button{ + background: #222; + color: #fff; + border: none; + font-size: 14px; + font-family: sans-serif; + padding: 4px 10px; +} + +.board-container{ + max-width: 960px; + margin: 0 auto; +} + +section.board{ + margin: 20px; + border: aliceblue; + background: #ffffff90; + border-radius: 3px; +} + +.board-header{ + height: 50px; + background: #fff; + border-radius: var(--border-radius) var(--border-radius) 0 0; +} + +.board-title{ + margin: 13px; + display: inline-block; + font-size: 20px; +} +.board-title, .board-add, .board-toggle{ + display: inline-block; +} + +.board-toggle{ + float: right; + margin: 13px; +} + +.board-columns{ + display: flex; + flex-wrap: nowrap; +} + +.board-column{ + padding: 10px; + flex: 1; +} + +.board-column-content{ + min-height: 49px; +} + +.board-column-content:empty{ + /*This only works if the tag is really empty and there is not even whitespace inside*/ + border: 4px solid #cdcdcd; + margin-top: 4px; + border-radius: 10px; + background: #eee; +} + +.board-column-title{ + text-align: center; +} + +.card{ + position: relative; + background: #222; + color: #fff; + border-radius: var(--border-radius); + margin: 4px 0; + padding: 4px; +} + +.board-column:nth-of-type(1) .card{ + background: var(--status-1); +} + +.board-column:nth-of-type(2) .card{ + background: var(--status-2); +} + +.board-column:nth-of-type(3) .card{ + background: var(--status-3); +} + +.board-column:nth-of-type(4) .card{ + background: var(--status-4); +} + +.card-remove{ + display: block; + position: absolute; + top: 4px; + right: 4px; + font-size: 12px; + cursor: pointer; +} + +.card-title{ + padding-right: 16px; +} diff --git a/static/css/main.css b/static/css/main.css deleted file mode 100644 index d8f3b84f..00000000 --- a/static/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -#boards { - background-color: green; -} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 48f40fe0..d56243d4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,24 +1,28 @@ - - - - - ProMan + + + + + ProMan - - - - + + + + - - - - -

    ProMan

    -
    Boards are loading... -
    + -
    - + + + +

    ProMan

    +
    + +
    +{% include 'template.html' %} + \ No newline at end of file diff --git a/templates/template.html b/templates/template.html new file mode 100644 index 00000000..6c5d277d --- /dev/null +++ b/templates/template.html @@ -0,0 +1,36 @@ +{#board template#} + + + +{#cards template#} + \ No newline at end of file From e37f8c0771044f3865cc880f1b95af65d53805ff Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Wed, 29 May 2019 14:30:19 +0200 Subject: [PATCH 08/34] bootstrap initialized --- static/js/dom.js | 2 +- templates/index.html | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/static/js/dom.js b/static/js/dom.js index b63268f5..363a7177 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -66,4 +66,4 @@ export let dom = { } } }, -}; +}; \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index d56243d4..6b29493e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,6 +6,10 @@ ProMan + + + + From 2640d5bca37e4eaa8ffb58d04734e54a3a801154 Mon Sep 17 00:00:00 2001 From: gergicsmilan Date: Thu, 30 May 2019 10:47:32 +0200 Subject: [PATCH 09/34] created post req function, implemented add new board function.. --- data_handler.py | 5 ++++- main.py | 10 +++++++++- persistence.py | 11 ++++++++++- static/js/data_handler.js | 16 ++++++++++++++++ static/js/dom.js | 16 +++++++++++++++- static/js/main.js | 4 +--- templates/index.html | 1 + 7 files changed, 56 insertions(+), 7 deletions(-) diff --git a/data_handler.py b/data_handler.py index 84e9db53..284d3479 100644 --- a/data_handler.py +++ b/data_handler.py @@ -24,5 +24,8 @@ def get_cards_for_board(board_id): def get_cards(): + return persistence.get_cards() - return persistence.get_cards() \ No newline at end of file + +def add_board(board_title): + return persistence.add_new_board(board_title) \ No newline at end of file diff --git a/main.py b/main.py index c513efb9..20ee8928 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, url_for +from flask import Flask, render_template, url_for, request, json from util import json_response import data_handler @@ -32,6 +32,7 @@ def get_cards_for_board(board_id): """ return data_handler.get_cards_for_board(board_id) + @app.route("/get-cards") @json_response def get_cards(): @@ -42,6 +43,13 @@ def get_cards(): return data_handler.get_cards() +@app.route("/post-new-board", methods=['POST', 'GET']) +@json_response +def post_new_board(): + response_data = json.loads(request.data) + data_handler.add_board(response_data['title']) + return response_data + def main(): app.run(debug=True) diff --git a/persistence.py b/persistence.py index 6c128f7f..a8b8539e 100644 --- a/persistence.py +++ b/persistence.py @@ -30,6 +30,7 @@ def get_cards(cursor): cards = cursor.fetchall() return cards + @connection.connection_handler def get_cards_for_board(cursor, board_id): cursor.execute("""SELECT id, board_id, title, status_id, card_order FROM card @@ -40,6 +41,7 @@ def get_cards_for_board(cursor, board_id): cards = cursor.fetchall() return cards + @connection.connection_handler def get_cards(cursor): cursor.execute("""SELECT id, board_id, title, status_id, card_order FROM card; @@ -47,4 +49,11 @@ def get_cards(cursor): cards = cursor.fetchall() print(cards) - return cards \ No newline at end of file + return cards + + +@connection.connection_handler +def add_new_board(cursor, board_title): + cursor.execute("""INSERT INTO board (title) VALUES (%(board_title)s); + """, + {'board_title': board_title}) \ No newline at end of file diff --git a/static/js/data_handler.js b/static/js/data_handler.js index 0e07d6e4..ee57f607 100644 --- a/static/js/data_handler.js +++ b/static/js/data_handler.js @@ -17,8 +17,18 @@ export let dataHandler = { .then(json_response => callback(json_response)); // Call the `callback` with the returned object }, _api_post: function (url, data, callback) { + // it is not called from outside // sends the data to the API, and calls callback function + + return fetch(url, { + method: 'POST', + mode: 'cors', + credentials: 'same-origin', + body: JSON.stringify(data), + }) + .then(response => response.json()) + .then(json_response => callback(json_response)); }, init: function () { }, @@ -54,6 +64,12 @@ export let dataHandler = { }, createNewBoard: function (boardTitle, callback) { // creates new board, saves it and calls the callback function with its data + let data = {'title': boardTitle}; + + this._api_post('/post-new-board', data, (response) => { + this._data = response; + callback(response); + }); }, createNewCard: function (cardTitle, boardId, statusId, callback) { // creates new card, saves it and calls the callback function with its data diff --git a/static/js/dom.js b/static/js/dom.js index 363a7177..3f41e97f 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -38,7 +38,6 @@ export let dom = { for (let column of columns) { column.setAttribute("data-board-id", board.id); } - clone.querySelector('.board').setAttribute("id", board.id); clone.querySelector('.board-title').innerHTML = board.title; boardContainer.appendChild(clone); dom.loadCards(parseInt(board.id)); @@ -66,4 +65,19 @@ export let dom = { } } }, + addNewBoard: function () { + + let addBoardButton = document.querySelector("#add-new-board"); + addBoardButton.addEventListener('click', function () { + dataHandler.createNewBoard('board', dom.showNewBoard); + }) + }, + showNewBoard: function(response){ + + let boardContainer = document.querySelector('.board-container'); + const template = document.querySelector('#board-template'); + const clone = document.importNode(template.content, true); + clone.querySelector('.board-title').innerHTML = response.title; + boardContainer.appendChild(clone); + } }; \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js index afff80c3..1379ba97 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -6,8 +6,6 @@ function init() { dom.init(); // loads the boards to the screen dom.loadBoards(); - - + dom.addNewBoard(); } - init(); diff --git a/templates/index.html b/templates/index.html index 6b29493e..a6095d71 100644 --- a/templates/index.html +++ b/templates/index.html @@ -24,6 +24,7 @@

    ProMan

    +
    From 6062e05a0be686d91f4c2e2770e0790ac9a0da6d Mon Sep 17 00:00:00 2001 From: kornelgatter Date: Thu, 30 May 2019 12:06:01 +0200 Subject: [PATCH 10/34] delete board button added --- static/css/design.css | 7 ++++++- templates/template.html | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/static/css/design.css b/static/css/design.css index a8b8f2f2..2563fbd1 100644 --- a/static/css/design.css +++ b/static/css/design.css @@ -55,7 +55,7 @@ section.board{ display: inline-block; font-size: 20px; } -.board-title, .board-add, .board-toggle{ +.board-title, .board-add, .board-toggle, .board-delete{ display: inline-block; } @@ -64,6 +64,11 @@ section.board{ margin: 13px; } +.board-delete{ + float:right; + margin: 13px; +} + .board-columns{ display: flex; flex-wrap: nowrap; diff --git a/templates/template.html b/templates/template.html index ee69ecfa..c151d147 100644 --- a/templates/template.html +++ b/templates/template.html @@ -4,6 +4,7 @@
    +
    From 786bea48c045cfcf2072a0e2218dfe5898962e95 Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Thu, 30 May 2019 13:11:36 +0200 Subject: [PATCH 11/34] adding new card feature is available for the user --- data_handler.py | 6 +++++- main.py | 7 +++++++ persistence.py | 12 +++++++++++- static/js/data_handler.js | 8 ++++++++ static/js/dom.js | 27 +++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/data_handler.py b/data_handler.py index 284d3479..b1e50671 100644 --- a/data_handler.py +++ b/data_handler.py @@ -28,4 +28,8 @@ def get_cards(): def add_board(board_title): - return persistence.add_new_board(board_title) \ No newline at end of file + return persistence.add_new_board(board_title) + + +def add_card(card_title, board_id, status_id): + return persistence.add_new_card(card_title, board_id, status_id) \ No newline at end of file diff --git a/main.py b/main.py index 20ee8928..180e80d3 100644 --- a/main.py +++ b/main.py @@ -50,6 +50,13 @@ def post_new_board(): data_handler.add_board(response_data['title']) return response_data +@app.route('/post-new-card', methods=['POST', 'GET']) +@json_response +def post_new_card(): + response_data = json.loads(request.data) + data_handler.add_card(response_data['title'], response_data['board_id'], response_data['status_id']) + return response_data + def main(): app.run(debug=True) diff --git a/persistence.py b/persistence.py index a8b8539e..31b9627e 100644 --- a/persistence.py +++ b/persistence.py @@ -56,4 +56,14 @@ def get_cards(cursor): def add_new_board(cursor, board_title): cursor.execute("""INSERT INTO board (title) VALUES (%(board_title)s); """, - {'board_title': board_title}) \ No newline at end of file + {'board_title': board_title}) + + +@connection.connection_handler +def add_new_card(cursor, card_title, board_id, status_id): + cursor.execute(""" + INSERT INTO card (title, board_id, status_id) VALUES (%(card_title)s, %(board_id)s, %(status_id)s); + """, + {'card_title': card_title, + 'board_id': board_id, + 'status_id': status_id}) \ No newline at end of file diff --git a/static/js/data_handler.js b/static/js/data_handler.js index ee57f607..0693c534 100644 --- a/static/js/data_handler.js +++ b/static/js/data_handler.js @@ -73,6 +73,14 @@ export let dataHandler = { }, createNewCard: function (cardTitle, boardId, statusId, callback) { // creates new card, saves it and calls the callback function with its data + let data = {'title': cardTitle, + 'board_id': boardId, + 'status_id': statusId}; + + this._api_post('/post-new-card', data, (response) => { + this._data = response; + callback(response); + }) }, // here comes more features getCards: function (callback) { diff --git a/static/js/dom.js b/static/js/dom.js index 3f41e97f..e63be096 100644 --- a/static/js/dom.js +++ b/static/js/dom.js @@ -39,6 +39,7 @@ export let dom = { column.setAttribute("data-board-id", board.id); } clone.querySelector('.board-title').innerHTML = board.title; + clone.querySelector('.board-add').setAttribute("id", board.id); boardContainer.appendChild(clone); dom.loadCards(parseInt(board.id)); } @@ -50,6 +51,7 @@ export let dom = { }, showCards: function (cards, boardId) { + dom.addNewCard(boardId); let cardContainer = document.querySelectorAll('.board-column-content'); for (let card of cards) { @@ -79,5 +81,30 @@ export let dom = { const clone = document.importNode(template.content, true); clone.querySelector('.board-title').innerHTML = response.title; boardContainer.appendChild(clone); + }, + addNewCard: function (boardId) { + let addNewCardButtons = document.querySelectorAll(".board-add"); + for (let button of addNewCardButtons) { + if (parseInt(button.id) === parseInt(boardId)) { + button.addEventListener('click', function () { + dataHandler.createNewCard("New Card", boardId, 0, dom.showNewCard); + }); + } + } + }, + showNewCard: function (response) { + //let boardId = response.board_id; + let columns = document.querySelectorAll('.board-column-content'); + + for (let column of columns) { + + if (parseInt(response.board_id) === parseInt(column.dataset.boardId) && + parseInt(response.status_id) === parseInt(column.id)) { + const template = document.querySelector('#cards-template'); + const clone = document.importNode(template.content, true); + clone.querySelector('.card-title').textContent = response.title; + column.appendChild(clone); + } + } } }; \ No newline at end of file From 9fc8f381a68a8a901b0b507b14ef09257045bef1 Mon Sep 17 00:00:00 2001 From: Fruzsina Kiss Date: Thu, 30 May 2019 13:16:11 +0200 Subject: [PATCH 12/34] renamed button to add new card --- templates/template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/template.html b/templates/template.html index ee69ecfa..5dfee379 100644 --- a/templates/template.html +++ b/templates/template.html @@ -2,7 +2,7 @@