mirror of
https://github.com/wassname/talk.git
synced 2026-07-01 18:58:29 +08:00
setting up dashboard :)
This commit is contained in:
@@ -8,6 +8,7 @@ import CommentStream from 'containers/CommentStream/CommentStream';
|
||||
import InstallContainer from 'containers/Install/InstallContainer';
|
||||
import CommunityContainer from 'containers/Community/CommunityContainer';
|
||||
import ModerationContainer from 'containers/ModerationQueue/ModerationContainer';
|
||||
import Dashboard from 'containers/Dashboard/Dashboard';
|
||||
|
||||
const routes = (
|
||||
<div>
|
||||
@@ -18,6 +19,7 @@ const routes = (
|
||||
<Route path='community' component={CommunityContainer} />
|
||||
<Route path='configure' component={Configure} />
|
||||
<Route path='streams' component={Streams} />
|
||||
<Route path='dashboard' component={Dashboard} />
|
||||
</Route>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,21 +13,35 @@ export default ({handleLogout, restricted = false}) => (
|
||||
!restricted ?
|
||||
<div>
|
||||
<Navigation className={styles.nav}>
|
||||
<IndexLink className={styles.navLink} to="/admin"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.moderate')}
|
||||
</IndexLink>
|
||||
<Link className={styles.navLink} to="/admin/community"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.community')}
|
||||
<IndexLink
|
||||
className={styles.navLink}
|
||||
to="/admin"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.moderate')}
|
||||
</IndexLink>
|
||||
<Link
|
||||
className={styles.navLink}
|
||||
to="/admin/community"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.community')}
|
||||
</Link>
|
||||
<Link className={styles.navLink} to="/admin/configure"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.configure')}
|
||||
<Link
|
||||
className={styles.navLink}
|
||||
to="/admin/configure"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.configure')}
|
||||
</Link>
|
||||
<Link className={styles.navLink} to="/admin/streams"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.streams')}
|
||||
<Link
|
||||
className={styles.navLink}
|
||||
to="/admin/streams"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.streams')}
|
||||
</Link>
|
||||
<Link
|
||||
className={styles.navLink}
|
||||
to="/admin/dashboard"
|
||||
activeClassName={styles.active}>
|
||||
{lang.t('configure.dashboard')}
|
||||
</Link>
|
||||
</Navigation>
|
||||
<div className={styles.rightPanel}>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import {compose} from 'react-apollo';
|
||||
import {mostFlags} from 'coral-admin/src/graphql/queries';
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div>Dashboard</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
mostFlags
|
||||
)(Dashboard);
|
||||
@@ -0,0 +1,4 @@
|
||||
import {graphql} from 'react-apollo';
|
||||
import MOST_FLAGS from './mostFlags.graphql';
|
||||
|
||||
export const mostFlags = graphql(MOST_FLAGS, {});
|
||||
@@ -0,0 +1,3 @@
|
||||
query mostFlags {
|
||||
metric
|
||||
}
|
||||
@@ -48,6 +48,7 @@
|
||||
"copy": "Copy to Clipboard"
|
||||
},
|
||||
"configure": {
|
||||
"dashboard": "Dashboard",
|
||||
"enable-pre-moderation": "Enable pre-moderation",
|
||||
"enable-pre-moderation-text": "Moderators must approve any comment before it is published.",
|
||||
"require-email-verification": "Require Email Verification",
|
||||
@@ -157,6 +158,7 @@
|
||||
"username_flags": ""
|
||||
},
|
||||
"configure": {
|
||||
"dashboard": "Panel",
|
||||
"enable-pre-moderation": "Habilitar pre-moderación",
|
||||
"enable-pre-moderation-text": "Los moderadores deben aprobar cada comentario antes de que sea publicado.",
|
||||
"require-email-verification": "Necesita confirmación de correo",
|
||||
|
||||
@@ -46,6 +46,15 @@ const findOrCreateAssetByURL = (context, asset_url) => {
|
||||
});
|
||||
};
|
||||
|
||||
const getAssetsForMetrics = ({loaders: {Actions, Comments}}) => {
|
||||
return Actions.getByTypes({action_type: 'FLAG', item_type: 'COMMENT'})
|
||||
.then((actions) => { // ALL ACTIONS :O
|
||||
const ids = actions.map(({item_id}) => item_id);
|
||||
|
||||
return Comments.getByQuery({ids});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a set of loaders based on a GraphQL context.
|
||||
* @param {Object} context the context of the GraphQL request
|
||||
@@ -59,6 +68,7 @@ module.exports = (context) => ({
|
||||
getByURL: (url) => findOrCreateAssetByURL(context, url),
|
||||
|
||||
getByID: new DataLoader((ids) => genAssetsByID(context, ids)),
|
||||
getForMetrics: () => getAssetsForMetrics(context),
|
||||
getAll: new util.SingletonResolver(() => AssetModel.find({}))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ const RootQuery = {
|
||||
// Map the actions from the items referenced byt this query. The actions
|
||||
// returned by this query are explicitly going to be distinct by their
|
||||
// `item_id`'s.
|
||||
let ids = actions.map((action) => action.item_id);
|
||||
let ids = actions.map(({item_id}) => item_id);
|
||||
|
||||
// Perform the query using the available resolver.
|
||||
return Comments.getByQuery({ids, statuses, asset_id, parent_id, limit, cursor, sort});
|
||||
@@ -44,6 +44,14 @@ const RootQuery = {
|
||||
return Comments.getByQuery(query);
|
||||
},
|
||||
|
||||
metric(_, args, {user, loaders: {Assets}}) {
|
||||
if (user == null || !user.hasRoles('ADMIN')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Assets.getForMetrics();
|
||||
},
|
||||
|
||||
// This returns the current user, ensure that if we aren't logged in, we
|
||||
// return null.
|
||||
me(_, args, {user}) {
|
||||
|
||||
@@ -167,6 +167,36 @@ interface ActionSummary {
|
||||
current_user: Action
|
||||
}
|
||||
|
||||
# A summary of actions for a specific action type on an Asset.
|
||||
interface AssetActionSummary {
|
||||
|
||||
# Number of actions associated with actionable types on this this Asset.
|
||||
actionCount: Int
|
||||
|
||||
# Number of unique actionable types that are referenced by the actions.
|
||||
actionableItemCount: Int
|
||||
}
|
||||
|
||||
# A summary of counts related to all the Likes on an Asset.
|
||||
type LikeAssetActionSummary implements AssetActionSummary {
|
||||
|
||||
# Number of actions associated with actionable types on this this Asset.
|
||||
actionCount: Int
|
||||
|
||||
# Number of unique actionable types that are referenced by the actions.
|
||||
actionableItemCount: Int
|
||||
}
|
||||
|
||||
# A summary of counts related to all the Flags on an Asset.
|
||||
type FlagAssetActionSummary implements AssetActionSummary {
|
||||
|
||||
# Number of actions associated with actionable types on this this Asset.
|
||||
actionCount: Int
|
||||
|
||||
# Number of unique actionable types that are referenced by the actions.
|
||||
actionableItemCount: Int
|
||||
}
|
||||
|
||||
# LikeAction is used by users who "like" a specific entity.
|
||||
type LikeAction implements Action {
|
||||
|
||||
@@ -294,6 +324,13 @@ type Asset {
|
||||
|
||||
# The date that the asset was created.
|
||||
created_at: Date
|
||||
|
||||
# Summary of all Actions against all entities associated with the Asset.
|
||||
# (likes, flags, etc.)
|
||||
action_summaries: [AssetActionSummary]
|
||||
|
||||
# Unique users that have commented on this Asset.
|
||||
authorCount: Int
|
||||
}
|
||||
|
||||
################################################################################
|
||||
@@ -358,6 +395,9 @@ type RootQuery {
|
||||
|
||||
# The currently logged in user based on the request.
|
||||
me: User
|
||||
|
||||
# metrics
|
||||
metric: [Asset]
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
||||
Reference in New Issue
Block a user