From d99bd3e215c4caa9c57ea8d76aec4a3fdf7dac0b Mon Sep 17 00:00:00 2001 From: Kyle Doherty <kyle.l.doherty@gmail.com> Date: Wed, 9 Sep 2015 17:58:17 -0700 Subject: [PATCH] full height sidebar and activity item refactor --- .../app/home/components/Activity.react.js | 163 +++++++++++++----- .../components/ActivityDescription.react.js | 156 ++--------------- .../app/home/components/ActivityItem.react.js | 46 +++++ .../home/components/ActivityStory.react.js | 24 +++ .../app/home/components/Homepage.react.js | 18 +- 5 files changed, 215 insertions(+), 192 deletions(-) create mode 100644 resources/frontend_client/app/home/components/ActivityItem.react.js create mode 100644 resources/frontend_client/app/home/components/ActivityStory.react.js diff --git a/resources/frontend_client/app/home/components/Activity.react.js b/resources/frontend_client/app/home/components/Activity.react.js index 1bf0afbb87e..31fc86e378d 100644 --- a/resources/frontend_client/app/home/components/Activity.react.js +++ b/resources/frontend_client/app/home/components/Activity.react.js @@ -9,6 +9,10 @@ import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper.r import { fetchActivity } from "../actions"; import ActivityDescription from "./ActivityDescription.react"; +import ActivityItem from './ActivityItem.react'; +import ActivityStory from './ActivityItem.react'; + +import Urls from "metabase/lib/urls"; export default class Activity extends Component { @@ -18,19 +22,115 @@ export default class Activity extends Component { this.state = { error: null, userColors: {} }; this.colorClasses = ['bg-brand', 'bg-purple', 'bg-error', 'bg-green', 'bg-gold', 'bg-grey-2']; + } - this.styles = { - modelLink: { - borderWidth: "2px" - }, - - initials: { - borderWidth: "0px", - borderStyle: "none" - } + userName(user, currentUser) { + if (user && user.id === currentUser.id) { + return "You"; + } else if (user) { + return user.first_name; + } else { + return "Metabase"; } } + activityDescription(item, user) { + + switch (item.topic) { + case "card-create": + return { + userName: this.userName(item.user, user), + subject: "saved a question about", + subjectRefLink: Urls.tableRowsQuery(item.database_id, item.table_id), + subjectRefName: item.table.display_name, + body: item.details.name, + bodyLink: Urls.modelToUrl(item.model, item.model_id), + timeSince: item.timestamp.fromNow() + }; + case "card-update": + return { + userName: this.userName(item.user, user), + subject: "saved a question about", + subjectRefLink: Urls.tableRowsQuery(item.database_id, item.table_id), + subjectRefName: item.table.display_name, + body: item.details.name, + bodyLink: Urls.modelToUrl(item.model, item.model_id), + timeSince: item.timestamp.fromNow() + }; + case "card-delete": + return { + userName: this.userName(item.user, user), + subject: "deleted a question", + subjectRefLink: null, + subjectRefName: null, + body: item.details.name, + bodyLink: Urls.modelToUrl(item.model, item.model_id), + timeSince: item.timestamp.fromNow() + }; + case "dashboard-create": + return { + userName: this.userName(item.user, user), + subject: "created a dashboard", + subjectRefLink: null, + subjectRefName: null, + body: item.details.name, + bodyLink: Urls.modelToUrl(item.model, item.model_id), + timeSince: item.timestamp.fromNow() + }; + case "dashboard-delete": + return { + userName: this.userName(item.user, user), + subject: "deleted a dashboard", + subjectRefLink: null, + subjectRefName: null, + body: item.details.name, + bodyLink: Urls.modelToUrl(item.model, item.model_id), + timeSince: item.timestamp.fromNow() + }; + case "dashboard-add-cards": + return { + userName: this.userName(item.user, user), + subject: "added a question to the dashboard -", + subjectRefLink: Urls.dashboard(item.model_id), + subjectRefName: item.details.name, + body: item.details.dashcards[0].name, + bodyLink: Urls.card(item.details.dashcards[0].card_id), + timeSince: item.timestamp.fromNow() + }; + case "dashboard-remove-cards": + return { + userName: this.userName(item.user, user), + subject: "removed a question from the dashboard -", + subjectRefLink: Urls.dashboard(item.model_id), + subjectRefName: item.details.name, + body: item.details.dashcards[0].name, + bodyLink: Urls.card(item.details.dashcards[0].card_id), + timeSince: item.timestamp.fromNow() + }; + case "database-sync": + return { + userName: this.userName(item.user, user), + subject: "received the latest data from", + subjectRefLink: null, + subjectRefName: item.database.name, + body: null, + bodyLink: null, + timeSince: item.timestamp.fromNow() + }; + case "user-joined": + return { + userName: this.userName(item.user, user), + subject: "joined the party!", + subjectRefLink: null, + subjectRefName: null, + body: null, + bodyLink: null, + timeSince: item.timestamp.fromNow() + }; + default: return "did some super awesome stuff thats hard to describe"; + }; + } + async componentDidMount() { try { await this.props.dispatch(fetchActivity()); @@ -66,19 +166,6 @@ export default class Activity extends Component { }); } - userInitials(user) { - let initials = '??'; - - if (user.first_name !== 'undefined') { - initials = user.first_name.substring(0, 1); - } - - if (user.last_name !== 'undefined') { - initials = initials + user.last_name.substring(0, 1); - } - - return initials; - } initialsCssClasses(user) { let { userColors } = this.state; @@ -98,28 +185,24 @@ export default class Activity extends Component { 'UserNick': true }); } - } renderActivity(activity) { return ( - <ul className="pt2 pb4"> - {activity.map(item => - <li key={item.id} className="flex pt2"> - <div className="mr3"> - {item.user ? - <span styles={this.styles.initials} className={this.initialsCssClasses(item.user)}> - <span className="UserInitials">{this.userInitials(item.user)}</span> - </span> - : - <span styles={this.styles.initials} className={this.initialsCssClasses(item.user)}> - <span className="UserInitials"><Icon name={'return'}></Icon></span> - </span> - } - </div> - <ActivityDescription item={item} user={this.props.user}></ActivityDescription> - </li> - )} + <ul className="pt2 pb4 relative"> + {activity.map(item => { + const description = this.activityDescription(item, item.user); + return ( + <li key={item.id} className="mt3"> + <ActivityItem + item={item} + description={description} + userColors={this.initialsCssClasses(item.user)} + /> + { description.body ? <ActivityStory story={description} /> : null } + </li> + ) + })} </ul> ); } diff --git a/resources/frontend_client/app/home/components/ActivityDescription.react.js b/resources/frontend_client/app/home/components/ActivityDescription.react.js index 9ac3ec2e107..7e4bdf50e93 100644 --- a/resources/frontend_client/app/home/components/ActivityDescription.react.js +++ b/resources/frontend_client/app/home/components/ActivityDescription.react.js @@ -2,160 +2,32 @@ import React, { Component, PropTypes } from "react"; -import Urls from "metabase/lib/urls"; - - export default class ActivityDescription extends Component { constructor(props) { super(props); - - this.styles = { - modelLink: { - borderWidth: "2px" - } - }; - } - - userName(user, currentUser) { - if (user && user.id === currentUser.id) { - return "You"; - } else if (user) { - return user.first_name; - } else { - return "Metabase"; - } - } - - activityDescription(item, user) { - - switch (item.topic) { - case "card-create": - return { - userName: this.userName(item.user, user), - subject: "saved a question about", - subjectRefLink: Urls.tableRowsQuery(item.database_id, item.table_id), - subjectRefName: item.table.display_name, - body: item.details.name, - bodyLink: Urls.modelToUrl(item.model, item.model_id), - timeSince: item.timestamp.fromNow() - }; - case "card-update": - return { - userName: this.userName(item.user, user), - subject: "saved a question about", - subjectRefLink: Urls.tableRowsQuery(item.database_id, item.table_id), - subjectRefName: item.table.display_name, - body: item.details.name, - bodyLink: Urls.modelToUrl(item.model, item.model_id), - timeSince: item.timestamp.fromNow() - }; - case "card-delete": - return { - userName: this.userName(item.user, user), - subject: "deleted a question", - subjectRefLink: null, - subjectRefName: null, - body: item.details.name, - bodyLink: Urls.modelToUrl(item.model, item.model_id), - timeSince: item.timestamp.fromNow() - }; - case "dashboard-create": - return { - userName: this.userName(item.user, user), - subject: "created a dashboard", - subjectRefLink: null, - subjectRefName: null, - body: item.details.name, - bodyLink: Urls.modelToUrl(item.model, item.model_id), - timeSince: item.timestamp.fromNow() - }; - case "dashboard-delete": - return { - userName: this.userName(item.user, user), - subject: "deleted a dashboard", - subjectRefLink: null, - subjectRefName: null, - body: item.details.name, - bodyLink: Urls.modelToUrl(item.model, item.model_id), - timeSince: item.timestamp.fromNow() - }; - case "dashboard-add-cards": - return { - userName: this.userName(item.user, user), - subject: "added a question to the dashboard -", - subjectRefLink: Urls.dashboard(item.model_id), - subjectRefName: item.details.name, - body: item.details.dashcards[0].name, - bodyLink: Urls.card(item.details.dashcards[0].card_id), - timeSince: item.timestamp.fromNow() - }; - case "dashboard-remove-cards": - return { - userName: this.userName(item.user, user), - subject: "removed a question from the dashboard -", - subjectRefLink: Urls.dashboard(item.model_id), - subjectRefName: item.details.name, - body: item.details.dashcards[0].name, - bodyLink: Urls.card(item.details.dashcards[0].card_id), - timeSince: item.timestamp.fromNow() - }; - case "database-sync": - return { - userName: this.userName(item.user, user), - subject: "received the latest data from", - subjectRefLink: null, - subjectRefName: item.database.name, - body: null, - bodyLink: null, - timeSince: item.timestamp.fromNow() - }; - case "user-joined": - return { - userName: this.userName(item.user, user), - subject: "joined the party!", - subjectRefLink: null, - subjectRefName: null, - body: null, - bodyLink: null, - timeSince: item.timestamp.fromNow() - }; - default: return "did some super awesome stuff thats hard to describe"; - }; } render() { - console.log('props=', this.props); - let { item, user } = this.props; - const description = this.activityDescription(item, user); - + let { description } = this.props; return ( - <div className="flex-full"> - <div className=""> - <div className="float-left text-grey-4"> - <span className="text-dark">{description.userName}</span> + <div className="ml2 full flex align-center"> + <div className="text-grey-4"> + <span className="text-dark">{description.userName}</span> - {description.subject} + {description.subject} - { description.subjectRefName && description.subjectRefLink ? - <a className="link text-dark" href={description.subjectRefLink}>{description.subjectRefName}</a> - : null } + { description.subjectRefName && description.subjectRefLink ? + <a className="link text-dark" href={description.subjectRefLink}>{description.subjectRefName}</a> + : null } - { description.subjectRefName && !description.subjectRefLink ? - <span className="text-dark">{description.subjectRefName}</span> - : null } - </div> - <div className="text-right text-grey-2"> - {description.timeSince} - </div> + { description.subjectRefName && !description.subjectRefLink ? + <span className="text-dark">{description.subjectRefName}</span> + : null } + </div> + <div className="flex-align-right text-right text-grey-2"> + {description.timeSince} </div> - { description.body ? - <div style={this.styles.modelLink} className="bordered rounded p2 mt1"> - <a className="link" href={description.bodyLink}>{description.body}</a> - </div> - : - null - } </div> ); } diff --git a/resources/frontend_client/app/home/components/ActivityItem.react.js b/resources/frontend_client/app/home/components/ActivityItem.react.js new file mode 100644 index 00000000000..adbc5f65c41 --- /dev/null +++ b/resources/frontend_client/app/home/components/ActivityItem.react.js @@ -0,0 +1,46 @@ +'use strict'; + +import React, { Component } from 'react'; + +export default class ActivityItem extends Component { + constructor() { + super() + this.styles = { + initials: { + borderRadius: '0px', + } + } + } + + userInitials(user) { + let initials = '??'; + + if (user.first_name !== 'undefined') { + initials = user.first_name.substring(0, 1); + } + + if (user.last_name !== 'undefined') { + initials = initials + user.last_name.substring(0, 1); + } + + return initials; + } + + render() { + const { item, description, userColors } = this.props + return ( + <div className="flex align-center"> + { item.user ? + <span styles={this.styles.initials} className={userColors}> + <span className="UserInitials">{this.userInitials(item.user)}</span> + </span> + : + <span styles={this.styles.initials} className={userColors}> + <span className="UserInitials"><Icon name={'return'}></Icon></span> + </span> + } + <ActivityDescription description={description} /> + </div> + ) + } +} diff --git a/resources/frontend_client/app/home/components/ActivityStory.react.js b/resources/frontend_client/app/home/components/ActivityStory.react.js new file mode 100644 index 00000000000..46a3cd27a82 --- /dev/null +++ b/resources/frontend_client/app/home/components/ActivityStory.react.js @@ -0,0 +1,24 @@ +'use strict'; + +import React, { Component } from 'react'; + +export default class ActivityStory extends Component { + constructor() { + super() + this.styles = { + modelLink: { + borderWidth: "2px" + }, + } + } + render() { + const { story } = this.props + return ( + <div className="ml2 mt1 border-left flex" style={{borderWidth: '3px'}}> + <div style={this.styles.modelLink} className="flex full ml4 bordered rounded p2"> + <a className="link" href={story.bodyLink}>{story.body}</a> + </div> + </div> + ) + } +} diff --git a/resources/frontend_client/app/home/components/Homepage.react.js b/resources/frontend_client/app/home/components/Homepage.react.js index cb372a0e322..6d275856a08 100644 --- a/resources/frontend_client/app/home/components/Homepage.react.js +++ b/resources/frontend_client/app/home/components/Homepage.react.js @@ -23,17 +23,15 @@ export default class Homepage extends Component { this.styles = { main: { - width: "auto", marginRight: "346px", - borderWidth: "2px" }, mainWrapper: { - width: "100%", - margin: "0 auto", - paddingLeft: "12em", - paddingRight: "3em" + maxWidth: "700px", + marginLeft: "auto", + marginRight: "auto", }, sidebar: { + borderWidth: "2px", width: "346px", backgroundColor: "#F9FBFC" }, @@ -48,7 +46,7 @@ export default class Homepage extends Component { return ( <div> - <div className="bg-brand text-white"> + <div className="bg-brand text-white pl4"> <div style={this.styles.main}> <div style={this.styles.mainWrapper}> <header style={this.styles.headerGreeting} className="flex align-center pb4"> @@ -64,8 +62,8 @@ export default class Homepage extends Component { </div> </div> </div> - <div className="relative"> - <div style={this.styles.main} className="border-right"> + <div className="relative pl4"> + <div style={this.styles.main}> <div style={this.styles.mainWrapper}> { selectedTab === 'activity' ? <Activity {...this.props} /> @@ -74,7 +72,7 @@ export default class Homepage extends Component { } </div> </div> - <div style={this.styles.sidebar} className="absolute top right"> + <div style={this.styles.sidebar} className="border-left absolute top right bottom"> { selectedTab === 'activity' ? <RecentViews {...this.props} /> : -- GitLab