Commit 5546bed5 by Joren Mathews

First commit

parents
.recognitions {
margin-right: 20px;
}
.recognitions.loading:before {
content: 'loading...';
font-style: italic;
}
.recognition {
text-decoration: none;
background-color: #fff;
display: block;
padding: 10px;
color: #555;
margin-bottom: 5px;
}
.recognition p {
font-size: 14px;
}
.recognition button {
float: right;
opacity: 1;
z-index: 1;
position: relative;
cursor: pointer;
display: none;
}
.recognition h5 {
font-style: italic;
}
.recognition p, .recognition h5 {
margin: 0;
}
.recognition.archived * {
opacity: .6;
}
.recognition.archived button {
display: block;
}
\ No newline at end of file
<?php
function recognitions_admin_page() {
wp_enqueue_script('wp-api');
wp_enqueue_style('recognitions-admin', plugin_dir_url(__FILE__) . 'admin.css');
require 'template.php';
}
add_action('admin_menu', 'register_values_board_admin_page');
function register_values_board_admin_page() {
add_menu_page('recognitions', 'Values Board', 'administrator', 'recognitions-admin-page', 'recognitions_admin_page');
}
// Messages
if (!function_exists('acf_add_local_field_group')):
function acf_missing_error_notice() {
?>
<div class="error notice">
<p><?php _e('The Values Board plugin requires the <a target="_blank" href="https://wordpress.org/plugins/advanced-custom-fields/">ACF plugin</a> to function.', 'values_board'); ?></p>
</div>
<?php
}
add_action('admin_notices', 'acf_missing_error_notice');
endif;
$options = get_option('values_board_options');
if (!$options || !$options['values_board_field_values']):
function values_missing_error_notice() {
?>
<div class="error notice">
<p><?php _e('You need to populate some values for the <a href="/wp-admin/admin.php?page=values_board">Values Board plugin</a>.', 'values_board'); ?></p>
</div>
<?php
}
add_action('admin_notices', 'values_missing_error_notice');
endif;
$users = get_users(array(
'role' => 'author'
));
if (!$users || !count($users)):
function users_missing_error_notice() {
?>
<div class="error notice">
<p><?php _e('You need to populate some <a href="/wp-admin/users.php">users</a> with the Author role for the Values Board plugin.', 'values_board'); ?></p>
</div>
<?php
}
add_action('admin_notices', 'users_missing_error_notice');
endif;
?>
\ No newline at end of file
<?php
function values_board_settings_init() {
register_setting('values_board', 'values_board_options');
add_settings_section(
'values_board_section_developers',
null,
'values_board_section_developers_cb',
'values_board'
);
// register a new field in the "values_board_section_developers" section, inside the "values_board" page
add_settings_field(
'values_board_field_values', // as of WP 4.6 this value is used only internally
// use $args' label_for to populate the id inside the callback
__('Values', 'values_board'),
'values_board_field_values_cb',
'values_board',
'values_board_section_developers',
[
'label_for' => 'values_board_field_values',
'class' => 'values_board_row',
'values_board_custom_data' => 'custom',
]
);
add_settings_field(
'values_board_field_colors', // as of WP 4.6 this value is used only internally
// use $args' label_for to populate the id inside the callback
__('Colors', 'values_board'),
'values_board_field_colors_cb',
'values_board',
'values_board_section_developers',
[
'label_for' => 'values_board_field_colors',
'class' => 'values_board_row',
'values_board_custom_data' => 'custom',
]
);
}
add_action('admin_init', 'values_board_settings_init');
function values_board_section_developers_cb($args) {
// HTML
}
function values_board_field_values_cb($args) {
$options = get_option('values_board_options');
?>
<textarea rows="10" cols="30" id="<?php echo esc_attr($args['label_for']); ?>" name="values_board_options[<?php echo esc_attr($args['label_for']); ?>]"><?php echo $options[$args['label_for']]; ?></textarea>
<p class="description">
<?php esc_html_e('Enter a list of values. One per line.', 'values_board'); ?>
</p>
<?php
}
function values_board_field_colors_cb($args) {
$options = get_option('values_board_options');
?>
<textarea rows="10" cols="15" id="<?php echo esc_attr($args['label_for']); ?>" name="values_board_options[<?php echo esc_attr($args['label_for']); ?>]"><?php echo $options[$args['label_for']]; ?></textarea>
<p class="description">
<?php esc_html_e('Enter a list of colors that correspond to the values above. One per line.', 'values_board'); ?>
</p>
<?php
}
function values_board_options_page() {
// add top level menu page
add_submenu_page(
'recognitions-admin-page',
'Settings',
'Settings',
'manage_options',
'values_board',
'values_board_options_page_html'
);
}
add_action('admin_menu', 'values_board_options_page');
function values_board_options_page_html() {
// check user capabilities
if (!current_user_can('manage_options')) {
return;
}
// add error/update messages
// check if the user have submitted the settings
// wordpress will add the "settings-updated" $_GET parameter to the url
if (isset($_GET['settings-updated'])) {
// add settings saved message with the class of "updated"
add_settings_error('values_board_messages', 'values_board_message', __('Settings Saved', 'values_board'), 'updated');
}
// show error/update messages
settings_errors('values_board_messages');
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
// output security fields for the registered setting "values_board"
settings_fields('values_board');
// output setting sections and their fields
// (sections are registered for "values_board", each field is registered to a specific section)
do_settings_sections('values_board');
// output save settings button
submit_button('Save Settings');
?>
</form>
</div>
<?php
}
\ No newline at end of file
<div class="wrap">
<h1>Recognitions Admin</h1>
<br>
</div>
<div class="controls_header">
<select id="year_filter">
<?php
$years = array(Date('Y'), Date('Y')-1);
foreach ($years as $i => $year) {
echo '<option value="' . $year . '">' . $year . '</option>';
} ?>
</select>
<select id="month_filter">
<?php
$months = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
foreach ($months as $i => $month) {
echo '<option ' . (Date('F') == $month ? 'selected="selected"' : '') . ' value="' . $month . '">' . $month . '</option>';
} ?>
</select>
<button id="archive_month">Archive Month</button>
</div>
<br>
<div class="recognitions">
</div>
<script>
jQuery(($) => {
var recognitionsWrapper = $('.recognitions');
var loadedRecognitions = null;
var currentMonth = null;
var currentYear = <?php echo Date('Y'); ?>;
var loadRecognitions = (year, month) => {
recognitionsWrapper.addClass('loading');
$.get('/values-board/archives?month=' + month + '&year=' + year, (response) => {
loadedRecognitions = $.parseJSON(response);
populateRecognitions(loadedRecognitions);
recognitionsWrapper.removeClass('loading');
});
}
// Debug
// var posts = new wp.api.collections.Posts();
// posts.fetch({data: {per_page: 100, status: 'any', filter: {'orderby': 'title', 'order': 'DESC'}}}).done(() => {
// console.log(posts);
// var loadedPosts = _.map(posts.models, post => post.attributes);
// });
var populateRecognitions = loadedRecognitions => {
recognitionsWrapper.html('');
loadedRecognitions.map(recognition => {
var recognitionElement = $('<a class="recognition"></a>');
recognitionElement.addClass(recognition.post_status == 'draft' ? 'archived' : '');
recognitionElement.attr('href', `/wp-admin/post.php?post=${recognition.ID}&action=edit`);
recognitionElement.attr('data-recognition-id', recognition.ID);
var note = recognition.acf.note;
var values = `<h5>${recognition.acf.value.split(',').join(', ')}</h5>`
var publishButton = '<button class="publish">Publish</button>';
recognitionElement.append(publishButton);
recognitionElement.append(note);
recognitionElement.append(values);
recognitionElement.appendTo(recognitionsWrapper);
});
}
var publishRecognition = (id) => {
$.post('/values-board/toggle-status?status=publish&id=' + id, (response) => {
$(`.recognition[data-recognition-id=${id}]`).removeClass('archived');
});
}
var archiveMonth = () => {
loadedRecognitions.map(loadedRecognition => {
$.post('/values-board/toggle-status?status=draft&id=' + loadedRecognition.ID, (response) => {
$(`.recognition[data-recognition-id=${loadedRecognition.ID}]`).addClass('archived');
});
});
}
$('.recognitions').on('click', 'button', (e) => {
var clicked = $(e.target);
var id = clicked.parent().attr('data-recognition-id');
publishRecognition(id);
return false;
});
$('#year_filter').change(e => {
currentYear = $(e.target).val();
loadRecognitions(currentYear, currentMonth);
});
$('#month_filter').change(e => {
currentMonth = $(e.target).val();
loadRecognitions(currentYear, currentMonth);
}).trigger('change');
$('#archive_month').click(e => {
archiveMonth();
});
})
</script>
\ No newline at end of file
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
\ No newline at end of file
node_modules/
\ No newline at end of file
var webpack = require('webpack');
var path = require('path');
module.exports = {
context: path.join(__dirname, 'app'),
entry: path.join(__dirname, 'app', 'entry.jsx'),
output: {
path: path.resolve(__dirname, "bin"),
filename: 'app.bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
plugins: [
new webpack.ProvidePlugin({
React: 'react',
ReactDOM: 'react-dom',
$: "jquery",
jQuery: "jquery"
})
],
watch: true
};
@import './vars.styl'
@import './imports/*'
@import './components/*'
#values_board
padding: 2rem 0
fixed: top 0 left 0
width: 100%
height: 100%
overflow: auto
font-family: 'Open Sans'
z-index: 40
bgimage('halftone.png', false)
*
font-family: 'Open Sans'
box-sizing: border-box
\ No newline at end of file
button.add
border-radius: 100%
background-color: $yellow
border: none
width: 2.2rem
height: 2.2rem
padding: 0
margin: 7px 0 0 0
position: relative
outline: none
cursor: pointer
&:after
content: '+'
centered()
font-size: 39px
font-weight: 300
color: black
top: 45%
#add
display: none
background-color: white
padding: 1rem 1rem .5rem 1rem
box-shadow: 3px 4px 5px 0px rgba(0,0,0,0.1)
width: 300px
margin: 1rem auto
img
width: 4rem
&.show
display: block
label
font-weight: bold
display: block
margin: .5rem 0 0 0
input[type="text"]
padding: 5px
font-size: 20px
border: 2px solid #eee
width: calc(100% - .8rem)
select
width: 100%
height: 7rem
input[type="submit"]
background-color: $yellow
color: black
padding: .5rem
margin: 1rem 0 .5rem 0
border: none
cursor: pointer
font-weight: bold
section.app
margin-top: 2rem
h1
font-size: 38px
font-weight: 300
margin: 3rem 0 0 2rem
img
width: 300px
height: auto
margin-right: 10px
\ No newline at end of file
.recognitions
padding: 50px 15px 15px 15px
overflow: hidden
display: flex
flex-wrap: wrap
justify-content: left
.recognition
background-color: white
padding: 5px 10px 10px 10px
margin-right: 15px
margin-top: 15px
flex: 0 0 240px
h2
margin: 0
font-size: 18px
.note
margin: 5px 0
p
margin: 0
font-size: 17px
.value
background-color: $yellow
color: white
padding: 4px 8px
margin-top: 5px
margin-right: 5px
border-radius: 3px
display: inline-block
text-transform: uppercase
font-size: 14px
font-weight: bold
&.color_0
background-color: #4f514f
&.color_1
background-color: #ce9d69
&.color_2
background-color: #1d7aa2
&.color_3
background-color: #6cc1b3
&.color_4
background-color: #f45e58
\ No newline at end of file
.user_wrapper
position: relative
min-height: 100px
margin: 20px 15px
box-shadow: 3px 4px 5px 0px rgba(0,0,0,0.1)
&:nth-child(odd)
background-color: rgba(20,20,20, .7)
&:nth-child(even)
background-color: rgba(20,20,20, .8)
.user
absolute: top 0 left 0
width: 100%
background-color: black
h2
color: white
font-size: 20px
padding: 5px 20px
white-space: nowrap
height: 40px
line-height: 40px
float: left
margin: 0
clear: none
img
width: 50px
height: 50px
float: left
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 293.26 45.78"><defs><style>.cls-1{fill:#000;}</style></defs><title>3FO_Logo_New</title><path class="cls-1" d="M22.78,20.39c3-1.64,6.53-4.59,6.53-9.47C29.31,5.58,24.85.19,14.88.19,10.19.19,4.61,2.14,2.43,3L2.31,3l2.4,5.63.11,0c6.08-2,11.18-2.16,14-.49a3.85,3.85,0,0,1,2,2.62,4.18,4.18,0,0,1-1.06,3.41c-1.92,2.33-5.89,3.74-10.61,3.78H9v6.21h.13c9.55.07,13,4,13,7.64a6.21,6.21,0,0,1-2.68,5.44C16.88,39,10,40.14,2.65,37.34l-.1,0L0,42.65l.12.05a32.71,32.71,0,0,0,13.34,3.06c11.09,0,18-7.11,18-13.71C31.41,26.53,28.51,22.61,22.78,20.39Z"/><path class="cls-1" d="M86.82,0A15.27,15.27,0,0,0,73.1,7.43c-2.5,4-3.88,9.47-3.88,15.49s1.38,11.47,3.9,15.42a15.29,15.29,0,0,0,13.7,7.36,15.6,15.6,0,0,0,13.77-7.39c2.56-4,4-9.48,4-15.46s-1.41-11.47-4-15.46A15.62,15.62,0,0,0,86.82,0Zm-.07,38.66c-6.11,0-9.08-5.15-9.08-15.74S80.64,7,86.75,7s9.36,5.32,9.36,15.81S93,38.66,86.75,38.66Z"/><path class="cls-1" d="M49.48,0A15.28,15.28,0,0,0,35.75,7.43c-2.5,4-3.87,9.47-3.87,15.49s1.38,11.47,3.9,15.42a15.29,15.29,0,0,0,13.7,7.36,15.63,15.63,0,0,0,13.77-7.39c2.56-4,4-9.48,4-15.46s-1.41-11.47-4-15.46A15.65,15.65,0,0,0,49.48,0Zm-.07,38.66c-6.11,0-9.08-5.15-9.08-15.74S43.3,7,49.41,7s9.36,5.32,9.36,15.81S55.62,38.66,49.41,38.66Z"/><path class="cls-1" d="M225.13,0A15.29,15.29,0,0,0,211.4,7.43c-2.5,4-3.87,9.47-3.87,15.49s1.38,11.47,3.9,15.42a15.29,15.29,0,0,0,13.7,7.36,15.6,15.6,0,0,0,13.77-7.39c2.56-4,4-9.48,4-15.46s-1.41-11.47-4-15.46A15.62,15.62,0,0,0,225.13,0Zm-.07,38.66c-6.11,0-9.08-5.15-9.08-15.74S219,7,225.06,7s9.36,5.32,9.36,15.81S231.27,38.66,225.06,38.66Z"/><polygon class="cls-1" points="199.3 9.55 199.3 10.44 199.3 10.44 199.3 9.55"/><path class="cls-1" d="M112.23,18.74s2.22-.07,2.22-4.41V8h17.91V1H106.67V45.43h7.78v-19h10.71v-7H112.23C111.54,19.34,111.83,18.74,112.23,18.74Z"/><path class="cls-1" d="M206.32,38.5l-.12,0c-3,.76-5,1-6,.29-.67-.52-.94-1.55-.94-3.57V19.17H205V13.45h-7.84c-.69,0-.4-.6,0-.6,0,0,2.09-.07,2.14-4.24V.76h-8V13.44h-2.1v5.73h2.1V35.53c0,4.61,1.59,10.1,9.15,10.1A19.63,19.63,0,0,0,207.71,44l.09,0Z"/><path class="cls-1" d="M291.78,38.5l-.12,0c-3,.76-5,1-6,.29-.66-.52-.94-1.55-.94-3.57V19.17h5.7V13.45h-7.84c-.69,0-.4-.6,0-.6,0,0,2.09-.07,2.14-4.24V.76h-8V13.44h-2.1v5.73h2.1V35.53c0,4.61,1.59,10.1,9.15,10.1A19.63,19.63,0,0,0,293.17,44l.09,0Z"/><path class="cls-1" d="M173.75,13.23c-5,0-9.13,1.67-11.87,4.82A16.82,16.82,0,0,0,158.1,29.3c0,10.42,5.78,16.4,15.86,16.4a28.28,28.28,0,0,0,12.5-3l.13-.07L185,36.79l-.11,0-.46.16c-1.71.62-7,2.37-10.51,2.37-5.71,0-6.89-4-7.26-7.93h20.65l0-.11c.89-6.26,0-11-2.65-14C182.39,14.6,178.7,13.23,173.75,13.23ZM167,25.57c.78-4,3.16-6,7.07-6,4.53,0,5.54,2.19,5.7,6Z"/><path class="cls-1" d="M142.09,13.23c-5,0-9.13,1.67-11.87,4.82a16.82,16.82,0,0,0-3.78,11.25c0,10.42,5.78,16.4,15.85,16.4a28.29,28.29,0,0,0,12.51-3l.13-.07-1.55-5.8-.12,0-.46.16c-1.71.62-7,2.37-10.51,2.37-5.7,0-6.88-4-7.25-7.93h20.65l0-.11c.89-6.26,0-11-2.66-14C150.73,14.6,147,13.23,142.09,13.23Zm-6.74,12.34c.79-4,3.17-6,7.08-6,4.53,0,5.54,2.19,5.7,6Z"/><path class="cls-1" d="M272.4,38.75c0-1.72,0-24,0-24h-8.24s0,17.41,0,18.5a5.15,5.15,0,0,1-1.29,4.07,7.45,7.45,0,0,1-5.58,2,3.83,3.83,0,0,1-3-1.35c-.86-1-.94-2.74-.9-4.34v-.07c0-.33,0-.67,0-1V14.7h-8.24V32.94c0,6,1.27,12.82,11.17,12.82A28,28,0,0,0,264,44.5c.1,0,.61-.22,1.44-.54l.32-.12a17.61,17.61,0,0,0,.75,1.85l0,.09,6.61-1.43,0-.13A30.54,30.54,0,0,1,272.4,38.75Z"/></svg>
\ No newline at end of file
// Mixins!
cover()
absolute: top 0 left 0
width: 100%
height: 100%
centered()
absolute: top 50% left 50%
transform: translate(-50%, -50%)
hcentered()
absolute: left 50%
transform: translateX(-50%)
vcentered()
absolute: top 50%
transform: translateY(-50%)
perheight(percent)
&:after
display: block
content: ' '
padding-top: percent
background($type)
if $type == 'cover'
background-size: cover
background-position: center center
background-repeat: no-repeat
if $type == 'contain'
background-size: contain
background-position: center center
background-repeat: no-repeat
bgimage($imageName, $absolute)
if $absolute
background-image: url('/wp-content/themes/threefo_values/css/images/' + $imageName)
else
background-image: url('images/' + $imageName)
darkoverlay()
&:before
content ''
position absolute
width 100%
height 100%
background-color rgba($black, 0.15)
\ No newline at end of file
$yellow = #e7ca27
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var plumber = require('gulp-plumber');
var cleanCSS = require('gulp-clean-css');
var stylus = require('gulp-stylus');
var axis = require('axis');
var jeet = require('jeet');
var rupture = require('rupture');
gulp.task('stylus', function() {
gulp.src('./css/app.styl')
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(stylus({use: [axis(), jeet(), rupture()], compress: true}))
.pipe(rename('site.min.css'))
.pipe(sourcemaps.write())
.pipe(gulp.dest('./css/'));
});
gulp.task('watch', function() {
gulp.watch('./css/**/*.styl', ['stylus']);
});
gulp.task('default', ['stylus', 'watch']);
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "SchedulingBoard",
"version": "0.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"flux": "^3.1.3",
"jquery": "^3.3.1",
"lodash": "^4.17.11",
"react": "^16.5.2",
"react-datepicker": "^1.6.0",
"react-dnd-html5-backend": "^5.0.1",
"react-dom": "^16.5.2",
"sifter": "^0.5.2"
},
"devDependencies": {
"@babel/core": "^7.1.0",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"axis": "^1.0.0",
"babel-loader": "^8.0.2",
"gulp": "^3.9.1",
"gulp-clean-css": "^3.10.0",
"gulp-concat": "^2.6.1",
"gulp-plumber": "^1.2.0",
"gulp-rename": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-stylus": "^2.7.0",
"gulp-uglify": "^3.0.1",
"jeet": "^7.2.0",
"rupture": "^0.7.1",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.0"
},
"scripts": {
"start": "webpack --mode development",
"build": "webpack --mode production"
},
"author": "",
"license": "ISC"
}
class Add extends React.Component {
constructor(props) {
super(props);
this.state = {show: false};
this.state.recognition = this.getEmptyRecognition();
}
getEmptyRecognition() {
return {
title: '',
note: '',
value: [],
who: ''
}
}
componentWillMount() {
this.addAction = this.addAction.bind(this);
emitter.on('add', this.addAction);
}
componentDidMount() {
}
componentWillUnmount() {
emitter.removeListener('add', this.addAction);
}
addAction(userData) {
this.state.recognition = this.getEmptyRecognition();
this.state.recognition.who = userData.ID;
this.toggle();
}
toggle() {
this.state.show = !this.state.show;
let newState = deepCopy(this.state);
this.setState(newState);
if (this.state.show) {
setTimeout(function() {
$('input[name="note"]').focus();
});
} else {
$('input[name="note"]').val('');
}
}
setValue(field, e) {
let newState = _.clone(this.state);
let value = e.target.value;
if (field == 'value') {
if (_.includes(newState.recognition.value, value)) {
newState.recognition.value = _.filter(newState.recognition.value, function(existingValue) {
return existingValue != value;
});
} else {
newState.recognition.value.push(value);
}
} else {
newState.recognition[field] = value;
}
this.setState(newState);
}
handleSubmit(e) {
e.preventDefault();
let recognitionData = {
title: this.state.recognition.note,
fields: {
note: this.state.recognition.note,
who: this.state.recognition.who,
value: this.state.recognition.value
}
}
dispatcher.dispatch({type: 'create-recognition', recognitionData: recognitionData});
this.state.recognition = this.getEmptyRecognition();
this.toggle();
$(window).focus();
}
render() {
let submitButton = <input type="submit" value="CREATE" />;
if (this.state.editedId)
submitButton = <input type="submit" value="SAVE" />;
let values = JSON.parse($('#values_choices').html());
let valuesMap = [];
_.each(values, function (value, key) {
valuesMap.push(
<option key={key} name={key} value={value}>{value}</option>
);
});
const user = _.find(this.props.users, (user) => {
return user.ID == this.state.recognition.who
});
let who = '';
if (this.state.recognition.who)
who = <img src={user.url} />
return (
<div id="add" className={this.state.show ? 'show' : ''}>
{who}
<form onSubmit={this.handleSubmit.bind(this)}>
<label htmlFor="note">Note</label>
<input type="text" id="note" onChange={this.setValue.bind(this, 'note')} value={this.state.recognition.name} name="note" />
<label htmlFor="value">Values</label>
<select multiple id="value" onChange={this.setValue.bind(this, 'value')} value={this.state.recognition.value} name="value">
{_.map(valuesMap, function(value) {
return value;
})}
</select>
<br />
{submitButton}
</form>
</div>
);
}
};
export default Add;
\ No newline at end of file
import HTML5Backend from 'react-dnd-html5-backend';
import User from './user.jsx';
import Add from './add.jsx';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {data: {users: null, recognitions: null}};
}
componentWillMount() {
this.dataReceived = this.dataReceived.bind(this);
emitter.on('data-received', this.dataReceived);
}
componentDidMount() {
dispatcher.dispatch({type: 'get-data'});
}
componentWillUnmount() {
emitter.removeListener('data-received', this.dataReceived);
}
dataReceived(data) {
this.setState({data: data});
}
render() {
const {users, recognitions} = this.state.data;
if (!users)
return null;
return (
<section className="app">
<h1><img src="/wp-content/plugins/values_board/page/css/images/logo.svg" /> Gold Star Board</h1>
<Add users={users} />
{users.map(function (user, i) {
return <User userData={user} recognitions={recognitions} key={i} />
}.bind(this))}
</section>
);
}
};
\ No newline at end of file
export default class Recognition extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const recognition = this.props.recognitionData;
let values = recognition.acf.value;
if (!Array.isArray(values))
values = [values];
return (
<div className="recognition">
<div className="note" dangerouslySetInnerHTML={{__html: recognition.acf.note}}></div>
{values.map(function(value, i) {
value = value.replace(/\W+/, '');
return (
<div key={i} className={'value color_' + i + ' color_' + value.toLowerCase()}>
{value}
</div>
);
})}
</div>
);
}
};
\ No newline at end of file
import Recognition from './recognition.jsx';
export default class Recognitions extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const {recognitions} = this.props;
return (
<div className="recognitions">
{recognitions.map(function (recognition, i) {
return (
<Recognition key={i} recognitionData={recognition} />
)
}.bind(this))}
</div>
);
}
};
\ No newline at end of file
import Recognitions from './recognitions.jsx';
export default class User extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.addRecognition = this.addRecognition.bind(this);
}
addRecognition() {
emitter.emit('add', this.props.userData);
}
render() {
const {userData, recognitions} = this.props;
let userRecognitions = _.filter(recognitions, function(u) {
if (!u.acf.who)
return false;
return u.acf.who.ID == userData.ID;
});
return (
<div className="user_wrapper">
<div className="user">
<img src={userData.url} />
<h2>{userData.display_name}</h2>
<button className="add" onClick={this.addRecognition}></button>
</div>
<Recognitions recognitions={userRecognitions} />
</div>
);
}
};
\ No newline at end of file
import React from 'react';
import ReactDOM from 'react-dom';
import Dispatcher from './lib/dispatcher.js';
import Emitter from './lib/emitter.js';
import './lib/helpers.js';
import ValuesData from './stores/values_data.js';
import App from './components/app.jsx';
ReactDOM.render(
<App />,
document.getElementById('values_board')
);
export const ItemTypes = {
TEAM_MEMBER: 'team_member',
TASK: 'task'
};
\ No newline at end of file
var Dispatcher = require('flux').Dispatcher;
window.dispatcher = new Dispatcher();
module.exports = window.dispatcher;
\ No newline at end of file
var EventEmitter = require('events').EventEmitter;
window.emitter = new EventEmitter();
emitter.setMaxListeners(50);
module.exports = window.dispatcher;
\ No newline at end of file
window.deepCopy = function(object) {
return JSON.parse(JSON.stringify(object));
}
window.changeColor = function(color, percent) {
var num = parseInt(color.slice(1),16), amt = Math.round(2.55 * percent), R = (num >> 16) + amt, G = (num >> 8 & 0x00FF) + amt, B = (num & 0x0000FF) + amt;
return "#" + (0x1000000 + (R<255?R<1?0:R:255)*0x10000 + (G<255?G<1?0:G:255)*0x100 + (B<255?B<1?0:B:255)).toString(16).slice(1);
}
window.selectElementText = function(el, win) {
win = win || window;
var doc = win.document, sel, range;
if (win.getSelection && doc.createRange) {
sel = win.getSelection();
range = doc.createRange();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
} else if (doc.body.createTextRange) {
range = doc.body.createTextRange();
range.moveToElementText(el);
range.select();
}
}
\ No newline at end of file
var ValuesStore = function() {
let _users = null;
let _recognitions = null;
dispatcher.register(function(payload) {
switch (payload.type) {
case 'get-data':
_getUsers();
_getRecognitions();
break;
// case 'update-task':
// _updateTask(payload.taskId, payload.changes);
// break;
case 'create-recognition':
_createRecognition(payload.recognitionData);
break;
}
}.bind(this));
var _getUsers = function() {
$.get('/values-board/users', function(usersResponse) {
_users = $.parseJSON(usersResponse);
_dataReceived();
});
}
var _getRecognitions = function() {
$.get('/values-board/recognitions', function(recognitionsResponse) {
_recognitions = $.parseJSON(recognitionsResponse);
_dataReceived();
});
}
var _createRecognition = function(recognitionData) {
recognitionData.author = window.current_user;
$.post('/values-board/recognition', recognitionData, function(createdResponse) {
const created = $.parseJSON(createdResponse);
_recognitions.push(created);
_dataReceived();
});
}
var _dataReceived = function() {
if (_users && _recognitions)
_notify('data-received', {users: _users, recognitions: _recognitions});
}
var _notify = function(ev, data) {
emitter.emit(ev, data);
}
}
module.exports = new ValuesStore();
\ No newline at end of file
var path = require('path');
var webpack = require('webpack');
module.exports = {
module: {
rules: [
{
test: /\.js|\.jsx$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
],
},
watch: true,
watchOptions: {
ignored: /node_modules/
},
plugins: [
new webpack.ProvidePlugin({
'React': 'react',
'$': 'jquery',
'_': 'lodash'
})
],
};
\ No newline at end of file
<?php
if (function_exists('acf_add_local_field_group')):
acf_add_local_field_group(array (
'key' => 'group_5a14aa156afe6',
'title' => 'Recognition Fields',
'fields' => array (
array (
'key' => 'field_5a14aa1c6d269',
'label' => 'Note',
'name' => 'note',
'type' => 'wysiwyg',
'value' => NULL,
'instructions' => '',
'required' => 1,
'conditional_logic' => 0,
'wrapper' => array (
'width' => '',
'class' => '',
'id' => '',
),
'default_value' => '',
'tabs' => 'all',
'toolbar' => 'full',
'media_upload' => 1,
'delay' => 0,
),
array (
'key' => 'field_5a14aa3b6d26a',
'label' => 'Value',
'name' => 'value',
'type' => 'textarea',
'value' => NULL,
'instructions' => '',
'required' => 1,
'conditional_logic' => 0,
'wrapper' => array (
'width' => '',
'class' => '',
'id' => '',
),
'default_value' => '',
'placeholder' => '',
'maxlength' => '',
'rows' => '',
'new_lines' => '',
),
array (
'key' => 'field_5a14aab76d26b',
'label' => 'Who',
'name' => 'who',
'type' => 'user',
'value' => NULL,
'instructions' => '',
'required' => 1,
'conditional_logic' => 0,
'wrapper' => array (
'width' => '',
'class' => '',
'id' => '',
),
'role' => '',
'allow_null' => 0,
'multiple' => 0,
),
),
'location' => array (
array (
array (
'param' => 'post_type',
'operator' => '==',
'value' => 'recognition',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'hide_on_screen' => '',
'active' => 1,
'description' => '',
));
acf_add_local_field_group(array (
'key' => 'group_5ba537363ab8f',
'title' => 'User Fields',
'fields' => array (
array (
'key' => 'field_5ba5374fb7c57',
'label' => 'Avatar',
'name' => 'avatar',
'type' => 'image',
'value' => NULL,
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array (
'width' => '',
'class' => '',
'id' => '',
),
'return_format' => 'array',
'preview_size' => 'thumbnail',
'library' => 'all',
'min_width' => '',
'min_height' => '',
'min_size' => '',
'max_width' => '',
'max_height' => '',
'max_size' => '',
'mime_types' => '',
),
),
'location' => array (
array (
array (
'param' => 'user_form',
'operator' => '==',
'value' => 'edit',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'hide_on_screen' => '',
'active' => 1,
'description' => '',
));
endif;
?>
\ No newline at end of file
<?php
function create_recognition_post_type() {
register_post_type('recognition',
array(
'labels' => array(
'name' => __('Recognitions'),
'singular_name' => __('Recognition')
),
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'recognitions'),
)
);
}
add_action('init', 'create_recognition_post_type');
?>
\ No newline at end of file
<?php
add_action('parse_request', 'parse_values_board_request');
function parse_values_board_request() {
global $current_user;
$uri = strtok($_SERVER["REQUEST_URI"], '?');
switch ($uri) {
case '/values-board/users':
if (!$current_user)
return null;
http_response_code(200);
$users = get_users(array(
'role' => 'author'
));
$returnArray = [];
foreach ($users as $user) {
$avatar = get_field('avatar', 'user_' . $user->ID);
$preppedUser = array(
'posts_per_page' => -1,
'ID' => $user->ID,
'display_name' => $user->data->display_name,
);
if ($avatar)
$preppedUser['url'] = $avatar['url'];
$returnArray[] = $preppedUser;
}
echo json_encode($returnArray);
exit();
case '/values-board/recognitions':
if (!$current_user)
return null;
http_response_code(200);
$recognitions = get_posts(array(
'posts_per_page' => -1,
'post_status' => 'any',
'post_type' => 'recognition'
));
foreach ($recognitions as $i => &$recognition) {
$fields = get_fields($recognition);
$fields['value'] = explode(',', $fields['value']);
$recognition->acf = $fields;
}
echo json_encode($recognitions);
exit();
case '/values-board/recognition':
if (!$current_user || !$_POST)
return null;
http_response_code(200);
$newPostId = wp_insert_post(array(
'posts_per_page' => -1,
'post_title' => $_POST['title'],
'post_author' => $_POST['author'],
'post_type' => 'recognition',
'post_status' => 'publish'
));
$values = implode(',', $_POST['fields']['value']);
update_field('note', $_POST['fields']['note'], $newPostId);
update_field('who', $_POST['fields']['who'], $newPostId);
update_field('value', $values, $newPostId);
$created = get_post($newPostId);
$created->acf = get_fields($created);
$created->acf['value'] = $_POST['fields']['value'];
echo json_encode($created);
exit();
case '/values-board/archives':
if (!$current_user)
return null;
http_response_code(200);
$recognitions = get_posts(array(
'posts_per_page' => -1,
'post_type' => 'recognition',
'post_status' => 'any',
'date_query' => array(
array(
'year' => $_GET['year'],
'month' => date('m', strtotime($_GET['month']))
),
),
));
foreach ($recognitions as $i => &$recognition) {
$fields = get_fields($recognition);
$recognition->acf = $fields;
}
echo json_encode($recognitions);
exit();
case '/values-board/toggle-status':
if (!$current_user)
return null;
http_response_code(200);
$id = $_GET['id'];
$status = $_GET['status'];
wp_update_post(array(
'ID' => $id,
'post_status' => $status
));
exit();
}
}
add_action('parse_request', 'avatar_fetcher');
function avatar_fetcher() {
$uri = strtok($_SERVER["REQUEST_URI"],'?');
if (strstr($uri, '/get-avatar-image')) {
$args = explode('/', $uri);
$uid = array_pop($args);
$dir = getcwd();
$avatarImg = get_avatar($uid);
preg_match('/src="(.*?)"/i', $avatarImg, $matches);
$avatarUrl = $matches[1];
header('Location: '. $avatarUrl);
exit();
}
}
?>
\ No newline at end of file
<?php
function board_shortcode($atts) {
if (in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1'))) {
wp_enqueue_script('values-board-js', plugin_dir_url(__FILE__) . '../page/dist/main.js', array(), $version_uid, TRUE);
} else {
}
wp_enqueue_style('values-board-fonts', 'https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Slabo+27px', array(), $version_uid);
wp_enqueue_style('values-board-css', plugin_dir_url(__FILE__) . '../page/css/site.min.css', array(), $version_uid);
$shortcode_content = ob_start();
include 'shortcode_template.php';
return ob_get_clean();
}
add_shortcode('values-board', 'board_shortcode');
?>
\ No newline at end of file
<?php
$options = get_option('values_board_options');
?>
<script>
window.current_user = <?php echo get_current_user_id(); ?>;
</script>
<script id="values_choices">
<?php
$values = $options['values_board_field_values'];
$colors = $options['values_board_field_colors'];
$exploded_values = explode(PHP_EOL, $values);
$exploded_colors = explode(PHP_EOL, $colors);
echo json_encode($exploded_values);
?>
</script>
<style>
<?php
foreach ($exploded_values as $i => $value) {
$value = preg_replace('/\W+/','',strtolower(strip_tags($value)));
echo '.value.color_' . $value . ' {background-color:' . $exploded_colors[$i] . '!important;}';
}
?>
</style>
<div id="values_board"></div>
\ No newline at end of file
<?php
/**
* Plugin Name: Values Board
* Description: Provides a board for people to leave recognitions for their coworkers.
* Author: 300FeetOut
* Author URI: http://300feetout.com
*/
defined('ABSPATH') or die("No script kiddies please!");
require('plumbing/post_type.php');
require('plumbing/routes.php');
require('plumbing/fields.php');
require('shortcode/shortcode.php');
require('admin/admin.php');
require('admin/settings.php');
?>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment