mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-01 21:52:19 +01:00
(merge) merge pre_inscription
This commit is contained in:
commit
cf132cf5cf
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,5 +1,20 @@
|
||||
# Changelog Fab-manager
|
||||
|
||||
## v6.0.8 2023 July 03
|
||||
|
||||
- Improved projects list filter
|
||||
- Fix a bug: unable to refresh machine/space/training calender after pay an reservation
|
||||
- Fix a bug: Accouning Line in duplicate
|
||||
- Fix a bug: displays "my orders" link only if store module is active
|
||||
- [TODO DEPLOY] `rails fablab:setup:build_accounting_lines`
|
||||
|
||||
## v6.0.7 2023 June 20
|
||||
|
||||
- Fix a bug: OpenAPI accounting gateway_object_id missing error
|
||||
- Fix a bug: unable to modify the price of prepaid pack
|
||||
- Fix a bug: notification type missing
|
||||
- Fix critical bug: Incorrect amount calculation when paying monthly subcription with a wallet for PayZen
|
||||
|
||||
## v6.0.6 2023 May 4
|
||||
|
||||
- Fix a bug: invalid duration for machine/spaces reservations in statistics, when using slots of not 1 hour
|
||||
|
4
Gemfile
4
Gemfile
@ -30,6 +30,7 @@ group :development, :test do
|
||||
# comment over to use visual debugger (eg. RubyMine), uncomment to use manual debugging
|
||||
# gem 'byebug'
|
||||
gem 'dotenv-rails'
|
||||
gem 'pry'
|
||||
end
|
||||
|
||||
group :development do
|
||||
@ -43,7 +44,6 @@ group :development do
|
||||
# Preview mail in the browser
|
||||
gem 'listen', '~> 3.0.5'
|
||||
gem 'overcommit'
|
||||
gem 'pry'
|
||||
gem 'rb-readline'
|
||||
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||
gem 'railroady'
|
||||
@ -149,3 +149,5 @@ gem 'acts_as_list'
|
||||
# Error reporting
|
||||
gem 'sentry-rails'
|
||||
gem 'sentry-ruby'
|
||||
|
||||
gem "reverse_markdown"
|
||||
|
@ -82,7 +82,7 @@ GEM
|
||||
rails (>= 4.1)
|
||||
ast (2.4.2)
|
||||
attr_required (1.0.1)
|
||||
awesome_print (1.8.0)
|
||||
awesome_print (1.9.2)
|
||||
axiom-types (0.1.1)
|
||||
descendants_tracker (~> 0.0.4)
|
||||
ice_nine (~> 0.11.0)
|
||||
@ -398,6 +398,8 @@ GEM
|
||||
responders (3.1.0)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
reverse_markdown (2.1.1)
|
||||
nokogiri
|
||||
rexml (3.2.5)
|
||||
rolify (5.3.0)
|
||||
rubocop (1.31.2)
|
||||
@ -590,6 +592,7 @@ DEPENDENCIES
|
||||
redis-session-store
|
||||
repost
|
||||
responders (~> 3.0)
|
||||
reverse_markdown
|
||||
rolify
|
||||
rubocop (~> 1.31)
|
||||
rubocop-rails
|
||||
|
45
app/controllers/api/project_categories_controller.rb
Normal file
45
app/controllers/api/project_categories_controller.rb
Normal file
@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# API Controller for resources of type ProjectCategory
|
||||
class API::ProjectCategoriesController < ApplicationController
|
||||
before_action :set_project_category, only: %i[update destroy]
|
||||
before_action :authenticate_user!, only: %i[create update destroy]
|
||||
def index
|
||||
@project_categories = ProjectCategory.all
|
||||
end
|
||||
|
||||
def create
|
||||
authorize ProjectCategory
|
||||
@project_category = ProjectCategory.new(project_category_params)
|
||||
if @project_category.save
|
||||
render json: @project_category, status: :created
|
||||
else
|
||||
render json: @project_category.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
authorize ProjectCategory
|
||||
if @project_category.update(project_category_params)
|
||||
render json: @project_category, status: :ok
|
||||
else
|
||||
render json: @project_category.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize ProjectCategory
|
||||
@project_category.destroy
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_project_category
|
||||
@project_category = ProjectCategory.find(params[:id])
|
||||
end
|
||||
|
||||
def project_category_params
|
||||
params.require(:project_category).permit(:name)
|
||||
end
|
||||
end
|
@ -18,6 +18,12 @@ class API::ProjectsController < API::APIController
|
||||
@project = Project.friendly.find(params[:id])
|
||||
end
|
||||
|
||||
def markdown
|
||||
@project = Project.friendly.find(params[:id])
|
||||
authorize @project
|
||||
send_data ProjectToMarkdown.new(@project).call, filename: "#{@project.name.parameterize}-#{@project.id}.md", disposition: 'attachment', type: 'text/markdown'
|
||||
end
|
||||
|
||||
def create
|
||||
@project = Project.new(project_params.merge(author_statistic_profile_id: current_user.statistic_profile.id))
|
||||
if @project.save
|
||||
@ -53,12 +59,23 @@ class API::ProjectsController < API::APIController
|
||||
|
||||
def search
|
||||
service = ProjectService.new
|
||||
res = service.search(params, current_user)
|
||||
paginate = request.format.zip? ? false : true
|
||||
res = service.search(params, current_user, paginate: paginate)
|
||||
|
||||
render json: res, status: :unprocessable_entity and return if res[:error]
|
||||
|
||||
@total = res[:total]
|
||||
@projects = res[:projects]
|
||||
render :index
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
@total = res[:total]
|
||||
@projects = res[:projects]
|
||||
render :index
|
||||
end
|
||||
format.zip do
|
||||
head :forbidden unless current_user.admin? || current_user.manager?
|
||||
|
||||
send_data ProjectsArchive.new(res[:projects]).call, filename: "projets.zip", disposition: 'attachment', type: 'application/zip'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
@ -69,7 +86,7 @@ class API::ProjectsController < API::APIController
|
||||
|
||||
def project_params
|
||||
params.require(:project).permit(:name, :description, :tags, :machine_ids, :component_ids, :theme_ids, :licence_id, :status_id, :state,
|
||||
user_ids: [], machine_ids: [], component_ids: [], theme_ids: [],
|
||||
user_ids: [], machine_ids: [], component_ids: [], theme_ids: [], project_category_ids: [],
|
||||
project_image_attributes: [:attachment],
|
||||
project_caos_attributes: %i[id attachment _destroy],
|
||||
project_steps_attributes: [
|
||||
|
25
app/frontend/src/javascript/api/project-category.ts
Normal file
25
app/frontend/src/javascript/api/project-category.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import apiClient from './clients/api-client';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { ProjectCategory } from '../models/project-category';
|
||||
|
||||
export default class ProjectCategoryAPI {
|
||||
static async index (): Promise<Array<ProjectCategory>> {
|
||||
const res: AxiosResponse<Array<ProjectCategory>> = await apiClient.get('/api/project_categories');
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async create (newProjectCategory: ProjectCategory): Promise<ProjectCategory> {
|
||||
const res: AxiosResponse<ProjectCategory> = await apiClient.post('/api/project_categories', { project_category: newProjectCategory });
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async update (updatedProjectCategory: ProjectCategory): Promise<ProjectCategory> {
|
||||
const res: AxiosResponse<ProjectCategory> = await apiClient.patch(`/api/project_categories/${updatedProjectCategory.id}`, { project_category: updatedProjectCategory });
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async destroy (projectCategoryId: number): Promise<void> {
|
||||
const res: AxiosResponse<void> = await apiClient.delete(`/api/project_categories/${projectCategoryId}`);
|
||||
return res?.data;
|
||||
}
|
||||
}
|
@ -115,17 +115,17 @@ export const ConfigurePacksButton: React.FC<ConfigurePacksButtonProps> = ({ pack
|
||||
itemId={p.id}
|
||||
itemType={t('app.admin.configure_packs_button.pack')}
|
||||
destroy={PrepaidPackAPI.destroy}/>
|
||||
<FabModal isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
title={t('app.admin.configure_packs_button.edit_pack')}
|
||||
className="edit-pack-modal"
|
||||
closeButton
|
||||
confirmButton={t('app.admin.configure_packs_button.confirm_changes')}
|
||||
onConfirmSendFormId="edit-pack">
|
||||
{packData && <PackForm formId="edit-pack" onSubmit={handleUpdate} pack={packData} />}
|
||||
</FabModal>
|
||||
</li>)}
|
||||
</ul>
|
||||
<FabModal isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
title={t('app.admin.configure_packs_button.edit_pack')}
|
||||
className="edit-pack-modal"
|
||||
closeButton
|
||||
confirmButton={t('app.admin.configure_packs_button.confirm_changes')}
|
||||
onConfirmSendFormId="edit-pack">
|
||||
{packData && <PackForm formId="edit-pack" onSubmit={handleUpdate} pack={packData} />}
|
||||
</FabModal>
|
||||
{packs?.length === 0 && <span>{t('app.admin.configure_packs_button.no_packs')}</span>}
|
||||
</FabPopover>}
|
||||
</div>
|
||||
|
@ -12,8 +12,8 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
Application.Controllers.controller('AdminProjectsController', ['$scope', '$state', 'Component', 'Licence', 'Theme', 'componentsPromise', 'licencesPromise', 'themesPromise', '_t', 'Member', 'uiTourService', 'settingsPromise', 'growl',
|
||||
function ($scope, $state, Component, Licence, Theme, componentsPromise, licencesPromise, themesPromise, _t, Member, uiTourService, settingsPromise, growl) {
|
||||
Application.Controllers.controller('AdminProjectsController', ['$scope', '$state', 'Component', 'Licence', 'Theme', 'ProjectCategory', 'componentsPromise', 'licencesPromise', 'themesPromise', 'projectCategoriesPromise', '_t', 'Member', 'uiTourService', 'settingsPromise', 'growl',
|
||||
function ($scope, $state, Component, Licence, Theme, ProjectCategory, componentsPromise, licencesPromise, themesPromise, projectCategoriesPromise, _t, Member, uiTourService, settingsPromise, growl) {
|
||||
// Materials list (plastic, wood ...)
|
||||
$scope.components = componentsPromise;
|
||||
|
||||
@ -23,6 +23,9 @@ Application.Controllers.controller('AdminProjectsController', ['$scope', '$state
|
||||
// Themes list (cooking, sport ...)
|
||||
$scope.themes = themesPromise;
|
||||
|
||||
// Project categories list (generic categorization)
|
||||
$scope.projectCategories = projectCategoriesPromise;
|
||||
|
||||
// Application settings
|
||||
$scope.allSettings = settingsPromise;
|
||||
|
||||
@ -115,6 +118,49 @@ Application.Controllers.controller('AdminProjectsController', ['$scope', '$state
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves a new project category / Update an existing project category to the server (form validation callback)
|
||||
* @param data {Object} project category name
|
||||
* @param [data] {number} project category id, in case of update
|
||||
*/
|
||||
$scope.saveProjectCategory = function (data, id) {
|
||||
if (id != null) {
|
||||
return ProjectCategory.update({ id }, data);
|
||||
} else {
|
||||
return ProjectCategory.save(data, resp => $scope.projectCategories[$scope.projectCategories.length - 1].id = resp.id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the project category at the specified index
|
||||
* @param index {number} project category index in the $scope.projectCategories array
|
||||
*/
|
||||
$scope.removeProjectCategory = function (index) {
|
||||
ProjectCategory.delete($scope.projectCategories[index]);
|
||||
return $scope.projectCategories.splice(index, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new empty entry in the $scope.projectCategories array
|
||||
*/
|
||||
$scope.addProjectCategory = function () {
|
||||
$scope.inserted = { name: '' };
|
||||
$scope.projectCategories.push($scope.inserted);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the newly inserted but not saved project category / Cancel the current project category modification
|
||||
* @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
|
||||
* @param index {number} project category index in the $scope.projectCategories array
|
||||
*/
|
||||
$scope.cancelProjectCategory = function (rowform, index) {
|
||||
if ($scope.projectCategories[index].id != null) {
|
||||
rowform.$cancel();
|
||||
} else {
|
||||
$scope.projectCategories.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves a new licence / Update an existing licence to the server (form validation callback)
|
||||
* @param data {Object} licence name and description
|
||||
|
@ -692,20 +692,9 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$tran
|
||||
* Refetch all events from the API and re-populate the calendar with the resulting slots
|
||||
*/
|
||||
const refreshCalendar = function () {
|
||||
const view = uiCalendarConfig.calendars.calendar.fullCalendar('getView');
|
||||
return Availability.machine({
|
||||
machineId: $scope.machine.id,
|
||||
member_id: $scope.ctrl.member.id,
|
||||
start: view.start,
|
||||
end: view.end,
|
||||
timezone: Fablab.timezone
|
||||
}, function (slots) {
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
|
||||
return $scope.eventSources.splice(0, 1, {
|
||||
events: slots,
|
||||
textColor: 'black'
|
||||
}
|
||||
);
|
||||
$scope.eventSources.splice(0, 1, {
|
||||
url: `/api/availabilities/machines/${$transition$.params().id}?member_id=${$scope.ctrl.member.id}`,
|
||||
textColor: 'black'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
* - $scope.themes = [{Theme}]
|
||||
* - $scope.licences = [{Licence}]
|
||||
* - $scope.allowedExtensions = [{String}]
|
||||
* - $scope.projectCategoriesWording = [{String}]
|
||||
* - $scope.submited(content)
|
||||
* - $scope.cancel()
|
||||
* - $scope.addFile()
|
||||
@ -43,7 +44,7 @@
|
||||
* - $state (Ui-Router) [ 'app.public.projects_show', 'app.public.projects_list' ]
|
||||
*/
|
||||
class ProjectsController {
|
||||
constructor ($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, Licence, Status, $document, Diacritics, dialogs, allowedExtensions, _t) {
|
||||
constructor ($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, ProjectCategory, Licence, Status, $document, Diacritics, dialogs, allowedExtensions, projectCategoriesWording, _t) {
|
||||
// remove codeview from summernote editor
|
||||
$scope.summernoteOptsProject = angular.copy($rootScope.summernoteOpts);
|
||||
$scope.summernoteOptsProject.toolbar[6][1].splice(1, 1);
|
||||
@ -78,6 +79,16 @@ class ProjectsController {
|
||||
});
|
||||
});
|
||||
|
||||
// Retrieve the list of themes from the server
|
||||
ProjectCategory.query().$promise.then(function (data) {
|
||||
$scope.projectCategories = data.map(function (d) {
|
||||
return ({
|
||||
id: d.id,
|
||||
name: d.name
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Retrieve the list of licences from the server
|
||||
Licence.query().$promise.then(function (data) {
|
||||
$scope.licences = data.map(function (d) {
|
||||
@ -104,6 +115,8 @@ class ProjectsController {
|
||||
// List of extensions allowed for CAD attachements upload
|
||||
$scope.allowedExtensions = allowedExtensions.setting.value.split(' ');
|
||||
|
||||
$scope.projectCategoriesWording = projectCategoriesWording.setting.value;
|
||||
|
||||
/**
|
||||
* For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
* Intended to be the callback when an upload is done: any raised error will be stacked in the
|
||||
@ -281,8 +294,8 @@ class ProjectsController {
|
||||
/**
|
||||
* Controller used on projects listing page
|
||||
*/
|
||||
Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'Project', 'machinesPromise', 'themesPromise', 'componentsPromise', 'paginationService', 'OpenlabProject', '$window', 'growl', '_t', '$location', '$timeout', 'settingsPromise', 'openLabActive',
|
||||
function ($scope, $state, Project, machinesPromise, themesPromise, componentsPromise, paginationService, OpenlabProject, $window, growl, _t, $location, $timeout, settingsPromise, openLabActive) {
|
||||
Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'Project', 'machinesPromise', 'themesPromise', 'projectCategoriesPromise', 'componentsPromise', 'paginationService', 'OpenlabProject', '$window', 'growl', '_t', '$location', '$timeout', 'settingsPromise', 'openLabActive', 'Member', 'Diacritics',
|
||||
function ($scope, $state, Project, machinesPromise, themesPromise, projectCategoriesPromise, componentsPromise, paginationService, OpenlabProject, $window, growl, _t, $location, $timeout, settingsPromise, openLabActive, Member, Diacritics) {
|
||||
/* PRIVATE STATIC CONSTANTS */
|
||||
|
||||
// Number of projects added to the page when the user clicks on 'load more projects'
|
||||
@ -294,12 +307,24 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
// Fab-manager's instance ID in the openLab network
|
||||
$scope.openlabAppId = settingsPromise.openlab_app_id;
|
||||
|
||||
// settings of optional filters
|
||||
$scope.memberFilterPresence = settingsPromise.projects_list_member_filter_presence !== 'false';
|
||||
$scope.dateFiltersPresence = settingsPromise.projects_list_date_filters_presence !== 'false';
|
||||
$scope.projectCategoriesFilterPlaceholder = settingsPromise.project_categories_filter_placeholder;
|
||||
|
||||
// Is openLab enabled on the instance?
|
||||
$scope.openlab = {
|
||||
projectsActive: openLabActive.isPresent,
|
||||
searchOverWholeNetwork: settingsPromise.openlab_default === 'true'
|
||||
};
|
||||
|
||||
if (!$scope.memberFilterPresence) {
|
||||
$location.$$search.member_id = '';
|
||||
}
|
||||
|
||||
fromDate = $location.$$search.from_date ? new Date($location.$$search.from_date) : undefined;
|
||||
toDate = $location.$$search.to_date ? new Date($location.$$search.to_date) : undefined;
|
||||
|
||||
// default search parameters
|
||||
$scope.search = {
|
||||
q: ($location.$$search.q || ''),
|
||||
@ -307,7 +332,27 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
machine_id: (parseInt($location.$$search.machine_id) || undefined),
|
||||
component_id: (parseInt($location.$$search.component_id) || undefined),
|
||||
theme_id: (parseInt($location.$$search.theme_id) || undefined),
|
||||
status_id: (parseInt($location.$$search.status_id) || undefined)
|
||||
status_id: (parseInt($location.$$search.status_id) || undefined),
|
||||
project_category_id: (parseInt($location.$$search.project_category_id) || undefined),
|
||||
member_id: (parseInt($location.$$search.member_id) || undefined),
|
||||
from_date: fromDate,
|
||||
to_date: toDate
|
||||
};
|
||||
|
||||
$scope.autoCompleteMemberName = function (nameLookup) {
|
||||
if (!nameLookup) {
|
||||
return;
|
||||
}
|
||||
$scope.isLoadingMembers = true;
|
||||
const asciiName = Diacritics.remove(nameLookup);
|
||||
|
||||
const q = { query: asciiName };
|
||||
|
||||
Member.search(q, function (users) {
|
||||
$scope.matchingMembers = users;
|
||||
$scope.isLoadingMembers = false;
|
||||
}
|
||||
, function (error) { console.error(error); });
|
||||
};
|
||||
|
||||
// list of projects to display
|
||||
@ -319,6 +364,9 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
// list of themes / used for filtering
|
||||
$scope.themes = themesPromise;
|
||||
|
||||
// list of projectCategories / used for filtering
|
||||
$scope.projectCategories = projectCategoriesPromise;
|
||||
|
||||
// list of components / used for filtering
|
||||
$scope.components = componentsPromise;
|
||||
|
||||
@ -332,6 +380,8 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
$scope.triggerSearch();
|
||||
};
|
||||
|
||||
$scope.zipUrl = '/api/projects/search.zip';
|
||||
|
||||
/**
|
||||
* Callback triggered when the button "search from the whole network" is toggled
|
||||
*/
|
||||
@ -361,6 +411,10 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
$scope.search.component_id = undefined;
|
||||
$scope.search.theme_id = undefined;
|
||||
$scope.search.status_id = undefined;
|
||||
$scope.search.member_id = undefined;
|
||||
$scope.search.from_date = undefined;
|
||||
$scope.search.to_date = undefined;
|
||||
$scope.search.project_category_id = undefined;
|
||||
$scope.$apply();
|
||||
$scope.setUrlQueryParams($scope.search);
|
||||
$scope.triggerSearch();
|
||||
@ -389,7 +443,10 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
} else {
|
||||
updateUrlParam('whole_network', 'f');
|
||||
$scope.projectsPagination = new paginationService.Instance(Project, currentPage, PROJECTS_PER_PAGE, null, { }, loadMoreCallback, 'search');
|
||||
Project.search({ search: $scope.search, page: currentPage, per_page: PROJECTS_PER_PAGE }, function (projectsPromise) {
|
||||
const fromDate = $scope.search.from_date ? $scope.search.from_date.toLocaleDateString() : undefined;
|
||||
const toDate = $scope.search.to_date ? $scope.search.to_date.toLocaleDateString() : undefined;
|
||||
const searchParams = Object.assign({}, $scope.search, { from_date: fromDate, to_date: toDate });
|
||||
Project.search({ search: searchParams, page: currentPage, per_page: PROJECTS_PER_PAGE }, function (projectsPromise) {
|
||||
$scope.projectsPagination.totalCount = projectsPromise.meta.total;
|
||||
$scope.projects = projectsPromise.projects;
|
||||
});
|
||||
@ -420,6 +477,22 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
updateUrlParam('component_id', search.component_id);
|
||||
updateUrlParam('machine_id', search.machine_id);
|
||||
updateUrlParam('status_id', search.status_id);
|
||||
updateUrlParam('member_id', search.member_id);
|
||||
const fromDate = search.from_date ? search.from_date.toDateString() : undefined;
|
||||
updateUrlParam('from_date', fromDate);
|
||||
const toDate = search.to_date ? search.to_date.toDateString() : undefined;
|
||||
updateUrlParam('to_date', toDate);
|
||||
updateUrlParam('project_category_id', search.project_category_id);
|
||||
$scope.zipUrl = '/api/projects/search.zip?' + new URLSearchParams({ search: JSON.stringify($location.search()) }).toString();
|
||||
return true;
|
||||
};
|
||||
|
||||
$scope.setSearchMemberId = function (searchMember) {
|
||||
if (searchMember) {
|
||||
$scope.search.member_id = searchMember.id;
|
||||
} else {
|
||||
$scope.search.member_id = undefined;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -450,6 +523,11 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
} else {
|
||||
$scope.openlab.searchOverWholeNetwork = $scope.openlab.projectsActive;
|
||||
}
|
||||
if ($location.$$search.member_id && $scope.memberFilterPresence) {
|
||||
Member.get({ id: $location.$$search.member_id }, function (member) {
|
||||
$scope.searchMember = member;
|
||||
});
|
||||
}
|
||||
return $scope.triggerSearch();
|
||||
};
|
||||
|
||||
@ -496,8 +574,8 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
|
||||
/**
|
||||
* Controller used in the project creation page
|
||||
*/
|
||||
Application.Controllers.controller('NewProjectController', ['$rootScope', '$scope', '$state', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'Licence', 'Status', '$document', 'CSRF', 'Diacritics', 'dialogs', 'allowedExtensions', '_t',
|
||||
function ($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, Licence, Status, $document, CSRF, Diacritics, dialogs, allowedExtensions, _t) {
|
||||
Application.Controllers.controller('NewProjectController', ['$rootScope', '$scope', '$state', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'ProjectCategory', 'Licence', 'Status', '$document', 'CSRF', 'Diacritics', 'dialogs', 'allowedExtensions', 'projectCategoriesWording', '_t',
|
||||
function ($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, ProjectCategory, Licence, Status, $document, CSRF, Diacritics, dialogs, allowedExtensions, projectCategoriesWording, _t) {
|
||||
CSRF.setMetaTags();
|
||||
|
||||
// API URL where the form will be posted
|
||||
@ -529,15 +607,15 @@ Application.Controllers.controller('NewProjectController', ['$rootScope', '$scop
|
||||
};
|
||||
|
||||
// Using the ProjectsController
|
||||
return new ProjectsController($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, Licence, Status, $document, Diacritics, dialogs, allowedExtensions, _t);
|
||||
return new ProjectsController($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, ProjectCategory, Licence, Status, $document, Diacritics, dialogs, allowedExtensions, projectCategoriesWording, _t);
|
||||
}
|
||||
]);
|
||||
|
||||
/**
|
||||
* Controller used in the project edition page
|
||||
*/
|
||||
Application.Controllers.controller('EditProjectController', ['$rootScope', '$scope', '$state', '$transition$', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'Licence', 'Status', '$document', 'CSRF', 'projectPromise', 'Diacritics', 'dialogs', 'allowedExtensions', '_t',
|
||||
function ($rootScope, $scope, $state, $transition$, Project, Machine, Member, Component, Theme, Licence, Status, $document, CSRF, projectPromise, Diacritics, dialogs, allowedExtensions, _t) {
|
||||
Application.Controllers.controller('EditProjectController', ['$rootScope', '$scope', '$state', '$transition$', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'ProjectCategory', 'Licence', 'Status', '$document', 'CSRF', 'projectPromise', 'Diacritics', 'dialogs', 'allowedExtensions', 'projectCategoriesWording', '_t',
|
||||
function ($rootScope, $scope, $state, $transition$, Project, Machine, Member, Component, Theme, ProjectCategory, Licence, Status, $document, CSRF, projectPromise, Diacritics, dialogs, allowedExtensions, projectCategoriesWording, _t) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// API URL where the form will be posted
|
||||
@ -583,7 +661,7 @@ Application.Controllers.controller('EditProjectController', ['$rootScope', '$sco
|
||||
}
|
||||
|
||||
// Using the ProjectsController
|
||||
return new ProjectsController($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, Licence, Status, $document, Diacritics, dialogs, allowedExtensions, _t);
|
||||
return new ProjectsController($rootScope, $scope, $state, Project, Machine, Member, Component, Theme, ProjectCategory, Licence, Status, $document, Diacritics, dialogs, allowedExtensions, projectCategoriesWording, _t);
|
||||
};
|
||||
|
||||
// !!! MUST BE CALLED AT THE END of the controller
|
||||
@ -594,14 +672,15 @@ Application.Controllers.controller('EditProjectController', ['$rootScope', '$sco
|
||||
/**
|
||||
* Controller used in the public project's details page
|
||||
*/
|
||||
Application.Controllers.controller('ShowProjectController', ['$scope', '$state', 'projectPromise', 'shortnamePromise', '$location', '$uibModal', 'dialogs', '_t',
|
||||
function ($scope, $state, projectPromise, shortnamePromise, $location, $uibModal, dialogs, _t) {
|
||||
Application.Controllers.controller('ShowProjectController', ['$scope', '$state', 'projectPromise', 'shortnamePromise', 'projectCategoriesWording', '$location', '$uibModal', 'dialogs', '_t',
|
||||
function ($scope, $state, projectPromise, shortnamePromise, projectCategoriesWording, $location, $uibModal, dialogs, _t) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// Store the project's details
|
||||
$scope.project = projectPromise;
|
||||
$scope.projectUrl = $location.absUrl();
|
||||
$scope.disqusShortname = shortnamePromise.setting.value;
|
||||
$scope.projectCategoriesWording = projectCategoriesWording.setting.value;
|
||||
|
||||
/**
|
||||
* Test if the provided user has the edition rights on the current project
|
||||
|
@ -609,20 +609,9 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$transi
|
||||
* Refetch all events from the API and re-populate the calendar with the resulting slots
|
||||
*/
|
||||
const refreshCalendar = function () {
|
||||
const view = uiCalendarConfig.calendars.calendar.fullCalendar('getView');
|
||||
return Availability.spaces({
|
||||
spaceId: $scope.space.id,
|
||||
member_id: $scope.ctrl.member.id,
|
||||
start: view.start,
|
||||
end: view.end,
|
||||
timezone: Fablab.timezone
|
||||
}, function (spaces) {
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
|
||||
return $scope.eventSources.splice(0, 1, {
|
||||
events: spaces,
|
||||
textColor: 'black'
|
||||
}
|
||||
);
|
||||
$scope.eventSources.splice(0, 1, {
|
||||
url: `/api/availabilities/spaces/${$transition$.params().id}?member_id=${$scope.ctrl.member.id}`,
|
||||
textColor: 'black'
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -385,20 +385,9 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$tra
|
||||
* Refetch all events from the API and re-populate the calendar with the resulting slots
|
||||
*/
|
||||
const refreshCalendar = function () {
|
||||
const view = uiCalendarConfig.calendars.calendar.fullCalendar('getView');
|
||||
const id = $transition$.params().id === 'all' ? $transition$.params().id : $scope.training.id;
|
||||
Availability.trainings({
|
||||
trainingId: id,
|
||||
member_id: $scope.ctrl.member.id,
|
||||
start: view.start,
|
||||
end: view.end,
|
||||
timezone: Fablab.timezone
|
||||
}, function (trainings) {
|
||||
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
|
||||
$scope.eventSources.splice(0, 1, {
|
||||
events: trainings,
|
||||
textColor: 'black'
|
||||
});
|
||||
$scope.eventSources.splice(0, 1, {
|
||||
url: `/api/availabilities/trainings/${$transition$.params().id}`,
|
||||
textColor: 'black'
|
||||
});
|
||||
}
|
||||
|
||||
|
5
app/frontend/src/javascript/models/project-category.ts
Normal file
5
app/frontend/src/javascript/models/project-category.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// Type model used in ProjectSettings and its child components
|
||||
export interface ProjectCategory {
|
||||
name: string,
|
||||
id?: number,
|
||||
}
|
@ -199,7 +199,11 @@ export const fabHubSettings = [
|
||||
export const projectsSettings = [
|
||||
'allowed_cad_extensions',
|
||||
'allowed_cad_mime_types',
|
||||
'disqus_shortname'
|
||||
'disqus_shortname',
|
||||
'projects_list_member_filter_presence',
|
||||
'projects_list_date_filters_presence',
|
||||
'project_categories_filter_placeholder',
|
||||
'project_categories_wording'
|
||||
] as const;
|
||||
|
||||
export const prepaidPacksSettings = [
|
||||
@ -222,7 +226,7 @@ export const pricingSettings = [
|
||||
'extended_prices_in_same_day'
|
||||
] as const;
|
||||
|
||||
export const poymentSettings = [
|
||||
export const paymentSettings = [
|
||||
'payment_gateway'
|
||||
] as const;
|
||||
|
||||
@ -292,7 +296,7 @@ export const allSettings = [
|
||||
...registrationSettings,
|
||||
...adminSettings,
|
||||
...pricingSettings,
|
||||
...poymentSettings,
|
||||
...paymentSettings,
|
||||
...displaySettings,
|
||||
...storeSettings,
|
||||
...trainingsSettings,
|
||||
|
@ -313,8 +313,9 @@ angular.module('application.router', ['ui.router'])
|
||||
themesPromise: ['Theme', function (Theme) { return Theme.query().$promise; }],
|
||||
componentsPromise: ['Component', function (Component) { return Component.query().$promise; }],
|
||||
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['openlab_app_id', 'openlab_default']" }).$promise; }],
|
||||
openLabActive: ['Setting', function (Setting) { return Setting.isPresent({ name: 'openlab_app_secret' }).$promise; }]
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['openlab_app_id', 'openlab_default', 'projects_list_member_filter_presence', 'projects_list_date_filters_presence', 'project_categories_filter_placeholder']" }).$promise; }],
|
||||
openLabActive: ['Setting', function (Setting) { return Setting.isPresent({ name: 'openlab_app_secret' }).$promise; }],
|
||||
projectCategoriesPromise: ['ProjectCategory', function (ProjectCategory) { return ProjectCategory.query().$promise; }]
|
||||
}
|
||||
})
|
||||
.state('app.logged.projects_new', {
|
||||
@ -326,7 +327,8 @@ angular.module('application.router', ['ui.router'])
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
allowedExtensions: ['Setting', function (Setting) { return Setting.get({ name: 'allowed_cad_extensions' }).$promise; }]
|
||||
allowedExtensions: ['Setting', function (Setting) { return Setting.get({ name: 'allowed_cad_extensions' }).$promise; }],
|
||||
projectCategoriesWording: ['Setting', function (Setting) { return Setting.get({ name: 'project_categories_wording' }).$promise; }]
|
||||
}
|
||||
})
|
||||
.state('app.public.projects_show', {
|
||||
@ -339,7 +341,8 @@ angular.module('application.router', ['ui.router'])
|
||||
},
|
||||
resolve: {
|
||||
projectPromise: ['$transition$', 'Project', function ($transition$, Project) { return Project.get({ id: $transition$.params().id }).$promise; }],
|
||||
shortnamePromise: ['Setting', function (Setting) { return Setting.get({ name: 'disqus_shortname' }).$promise; }]
|
||||
shortnamePromise: ['Setting', function (Setting) { return Setting.get({ name: 'disqus_shortname' }).$promise; }],
|
||||
projectCategoriesWording: ['Setting', function (Setting) { return Setting.get({ name: 'project_categories_wording' }).$promise; }]
|
||||
}
|
||||
})
|
||||
.state('app.logged.projects_edit', {
|
||||
@ -352,7 +355,8 @@ angular.module('application.router', ['ui.router'])
|
||||
},
|
||||
resolve: {
|
||||
projectPromise: ['$transition$', 'Project', function ($transition$, Project) { return Project.get({ id: $transition$.params().id }).$promise; }],
|
||||
allowedExtensions: ['Setting', function (Setting) { return Setting.get({ name: 'allowed_cad_extensions' }).$promise; }]
|
||||
allowedExtensions: ['Setting', function (Setting) { return Setting.get({ name: 'allowed_cad_extensions' }).$promise; }],
|
||||
projectCategoriesWording: ['Setting', function (Setting) { return Setting.get({ name: 'project_categories_wording' }).$promise; }]
|
||||
}
|
||||
})
|
||||
|
||||
@ -747,10 +751,11 @@ angular.module('application.router', ['ui.router'])
|
||||
componentsPromise: ['Component', function (Component) { return Component.query().$promise; }],
|
||||
licencesPromise: ['Licence', function (Licence) { return Licence.query().$promise; }],
|
||||
themesPromise: ['Theme', function (Theme) { return Theme.query().$promise; }],
|
||||
projectCategoriesPromise: ['ProjectCategory', function (ProjectCategory) { return ProjectCategory.query().$promise; }],
|
||||
settingsPromise: ['Setting', function (Setting) {
|
||||
return Setting.query({
|
||||
names: "['feature_tour_display', 'disqus_shortname', 'allowed_cad_extensions', " +
|
||||
"'allowed_cad_mime_types', 'openlab_app_id', 'openlab_app_secret', 'openlab_default']"
|
||||
"'allowed_cad_mime_types', 'openlab_app_id', 'openlab_app_secret', 'openlab_default', 'project_categories_filter_placeholder', 'project_categories_wording']"
|
||||
}).$promise;
|
||||
}]
|
||||
}
|
||||
|
11
app/frontend/src/javascript/services/project_category.js
Normal file
11
app/frontend/src/javascript/services/project_category.js
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
Application.Services.factory('ProjectCategory', ['$resource', function ($resource) {
|
||||
return $resource('/api/project_categories/:id',
|
||||
{ id: '@id' }, {
|
||||
update: {
|
||||
method: 'PUT'
|
||||
}
|
||||
}
|
||||
);
|
||||
}]);
|
@ -42,7 +42,10 @@
|
||||
<uib-tab heading="{{ 'app.admin.projects.statuses' | translate }}" index="3">
|
||||
<status-settings on-error="onError" on-success="onSuccess"/>
|
||||
</uib-tab>
|
||||
<uib-tab heading="{{ 'app.admin.projects.settings.title' | translate }}" index="4" class="settings-tab">
|
||||
<uib-tab heading="{{ 'app.admin.projects.project_categories' | translate }}" index="4">
|
||||
<ng-include src="'/admin/projects/project_categories.html'"></ng-include>
|
||||
</uib-tab>
|
||||
<uib-tab heading="{{ 'app.admin.projects.settings.title' | translate }}" index="5" class="settings-tab">
|
||||
<ng-include src="'/admin/projects/settings.html'"></ng-include>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
|
@ -0,0 +1,38 @@
|
||||
<button type="button" class="btn btn-warning m-t m-b" ng-click="addProjectCategory()" translate>{{ 'app.admin.projects.add_a_new_project_category' }}</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:80%" translate>{{ 'app.admin.project_categories.name' }}</th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="projectCategory in projectCategories">
|
||||
<td>
|
||||
<span editable-text="projectCategory.name" e-name="name" e-form="rowform" e-required>
|
||||
{{ projectCategory.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<!-- form -->
|
||||
<form editable-form name="rowform" onbeforesave="saveProjectCategory($data, projectCategory.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == projectCategory">
|
||||
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-warning">
|
||||
<i class="fa fa-check"></i>
|
||||
</button>
|
||||
<button type="button" ng-disabled="rowform.$waiting" ng-click="cancelProjectCategory(rowform, $index)" class="btn btn-default">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
<div class="buttons" ng-show="!rowform.$visible">
|
||||
<button class="btn btn-default" ng-click="rowform.$show()">
|
||||
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="removeProjectCategory($index)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
@ -95,3 +95,49 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default m-t-lg">
|
||||
<div class="panel-heading">
|
||||
<span class="font-sbold" translate>{{ 'app.admin.projects.settings.filters' }}</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<boolean-setting name="'projects_list_member_filter_presence'"
|
||||
label="'app.admin.settings.projects_list_member_filter_presence' | translate"
|
||||
on-success="onSuccess"
|
||||
on-error="onError"
|
||||
class-name="'m-l'"></boolean-setting>
|
||||
</div>
|
||||
<div class="row">
|
||||
<boolean-setting name="'projects_list_date_filters_presence'"
|
||||
label="'app.admin.settings.projects_list_date_filters_presence' | translate"
|
||||
on-success="onSuccess"
|
||||
on-error="onError"
|
||||
class-name="'m-l'"></boolean-setting>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default m-t-lg">
|
||||
<div class="panel-heading">
|
||||
<span class="font-sbold" translate>{{ 'app.admin.projects.settings.project_categories' }}</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<text-setting name="project_categories_filter_placeholder"
|
||||
settings="allSettings"
|
||||
label="app.admin.settings.project_categories_filter_placeholder">
|
||||
</text-setting>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t">
|
||||
<div class="col-md-6">
|
||||
<text-setting name="project_categories_wording"
|
||||
settings="allSettings"
|
||||
label="app.admin.settings.project_categories_wording">
|
||||
</text-setting>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -279,6 +279,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3 translate>{{ projectCategoriesWording }}</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<input type="hidden" name="project[project_category_ids][]" value="" />
|
||||
<ui-select multiple ng-model="project.project_category_ids" class="form-control">
|
||||
<ui-select-match>
|
||||
<span ng-bind="$item.name"></span>
|
||||
<input type="hidden" name="project[project_category_ids][]" value="{{$item.id}}" />
|
||||
</ui-select-match>
|
||||
<ui-select-choices repeat="pc.id as pc in (projectCategories | filter: $select.search)">
|
||||
<span ng-bind-html="pc.name | highlight: $select.search"></span>
|
||||
</ui-select-choices>
|
||||
</ui-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<section class="projects">
|
||||
<div class="projects-filters">
|
||||
<header>
|
||||
<h3>Filter</h3>
|
||||
<h3 translate>{{ 'app.public.projects_list.filter' }}</h3>
|
||||
<a href="javascript:void(0);" class="fab-button is-black" name="button" ng-click="resetFiltersAndTriggerSearch()" ng-show="!openlab.searchOverWholeNetwork">{{ 'app.public.projects_list.reset_all_filters' | translate }}</a>
|
||||
</header>
|
||||
<span class="switch" ng-if="openlab.projectsActive" uib-tooltip="{{ 'app.public.projects_list.tooltip_openlab_projects_switch' | translate }}" tooltip-trigger="mouseenter">
|
||||
@ -61,12 +61,57 @@
|
||||
<option value="" translate>{{ 'app.public.projects_list.all_themes' }}</option>
|
||||
</select>
|
||||
|
||||
<select ng-model="search.project_category_id" ng-change="setUrlQueryParams(search) && triggerSearch()" class="form-control" ng-options="pc.id as pc.name for pc in projectCategories">
|
||||
<option value="" translate>{{ projectCategoriesFilterPlaceholder }}</option>
|
||||
</select>
|
||||
|
||||
<select ng-model="search.component_id" ng-change="setUrlQueryParams(search) && triggerSearch()" class="form-control" ng-options="t.id as t.name for t in components">
|
||||
<option value="" translate>{{ 'app.public.projects_list.all_materials' }}</option>
|
||||
</select>
|
||||
|
||||
<ui-select ng-if="currentUser && memberFilterPresence" ng-model="searchMember" on-select="setSearchMemberId(searchMember) && setUrlQueryParams(search) && triggerSearch()">
|
||||
<ui-select-match allow-clear="true" placeholder="{{ 'app.public.projects_list.filter_by_member' | translate }}">
|
||||
<span ng-bind="$select.selected.name"></span>
|
||||
</ui-select-match>
|
||||
<ui-select-choices repeat="m in matchingMembers" refresh="autoCompleteMemberName($select.search)" refresh-delay="300">
|
||||
<span ng-bind-html="m.name | highlight: $select.search"></span>
|
||||
</ui-select-choices>
|
||||
</ui-select>
|
||||
|
||||
<label class="form-group m-n" ng-if="dateFiltersPresence">
|
||||
<div class="form-item-header">
|
||||
<p translate>{{ 'app.public.projects_list.created_from' }}</p>
|
||||
</div>
|
||||
<input class="form-control"
|
||||
ng-model="search.from_date"
|
||||
ng-model-options='{ debounce: 1000 }'
|
||||
ng-change="setUrlQueryParams(search) && triggerSearch()"
|
||||
type="date"
|
||||
min="2000-01-01"
|
||||
max="2060-01-01"/>
|
||||
</label>
|
||||
|
||||
<label class="form-group m-n" ng-if="dateFiltersPresence">
|
||||
<div class="form-item-header">
|
||||
<p translate>{{ 'app.public.projects_list.created_to' }}</p>
|
||||
</div>
|
||||
<input class="form-control"
|
||||
ng-model="search.to_date"
|
||||
ng-model-options='{ debounce: 1000 }'
|
||||
ng-change="setUrlQueryParams(search) && triggerSearch()"
|
||||
type="date"
|
||||
min="2000-01-01"
|
||||
max="2060-01-01"/>
|
||||
</label>
|
||||
|
||||
<status-filter on-filter-change="onStatusChange" current-status-index="search.status_id"/>
|
||||
</div>
|
||||
|
||||
<div class="text-center m m-b-lg" ng-if="!openlab.searchOverWholeNetwork && (projects.length != 0) && (isAuthorized('admin') || isAuthorized('manager'))">
|
||||
<a class="btn bg-light text-black" ng-href="{{ zipUrl }}" target="_blank">
|
||||
<i class="fa fa-download"></i> {{ 'app.public.projects_list.download_archive' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="projects-list">
|
||||
|
@ -1,187 +1,202 @@
|
||||
<div>
|
||||
|
||||
<section class="heading b-b">
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a ng-click="backPrevLocation($event)"><i class="fas fa-long-arrow-alt-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ project.name }} <span class="badge" ng-if="project.state == 'draft'" translate>{{ 'app.public.projects_show.rough_draft' }}</span></h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
|
||||
<a ui-sref="app.logged.projects_edit({id: project.id})" ng-if="projectEditableBy(currentUser) || isAuthorized('admin')" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs text-u-c text-sm"><i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}</a>
|
||||
<a ng-click="deleteProject(event)" ng-if="projectDeletableBy(currentUser) || isAuthorized('admin')" class="btn btn-lg btn-danger b-2x rounded no-b m-t-xs"><i class="fa fa-trash-o"></i></a>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a ng-click="backPrevLocation($event)"><i class="fas fa-long-arrow-alt-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ project.name }} <span class="badge" ng-if="project.state == 'draft'" translate>{{ 'app.public.projects_show.rough_draft' }}</span></h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
|
||||
<a ui-sref="app.logged.projects_edit({id: project.id})" ng-if="projectEditableBy(currentUser) || isAuthorized('admin')" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs text-u-c text-sm"><i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}</a>
|
||||
<a ng-click="deleteProject(event)" ng-if="projectDeletableBy(currentUser) || isAuthorized('admin')" class="btn btn-lg btn-danger b-2x rounded no-b m-t-xs"><i class="fa fa-trash-o"></i></a>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 b-r-lg">
|
||||
|
||||
<div class="article wrapper-lg">
|
||||
|
||||
<div class="article-thumbnail" ng-if="project.project_image">
|
||||
<a href="{{project.project_full_image}}" target="_blank"><img ng-src="{{project.project_image}}" alt="{{project.name}}"></a>
|
||||
</div>
|
||||
|
||||
<h3 translate>{{ 'app.public.projects_show.project_description' }}</h3>
|
||||
<p ng-bind-html="project.description | toTrusted"></p>
|
||||
|
||||
<div class="article-steps">
|
||||
<div class="row article-step m-b-lg" ng-repeat="step in project.project_steps_attributes">
|
||||
<div class="col-md-12 m-b-xs">
|
||||
<h3 class="well well-simple step-title">{{ 'app.public.projects_show.step_N' | translate:{INDEX:step.step_nb} }} : {{step.title}}</h3>
|
||||
</div>
|
||||
<div ng-repeat-start="image in step.project_step_images_attributes" class="clearfix" ng-if="$index % 3 == 0"></div>
|
||||
<div class="col-md-4" ng-repeat-end>
|
||||
<a href="{{image.attachment_full_url}}" target="_blank"><img class="m-b" ng-src="{{image.attachment_url}}" alt="{{image.attachment}}" ></a>
|
||||
</div>
|
||||
<div class="col-md-8" ng-class="{'col-md-12' : step.project_step_images_attributes.length > 1 || step.project_step_images_attributes.length == 0}">
|
||||
|
||||
<p ng-bind-html="step.description | toTrusted"></p>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 b-r-lg">
|
||||
|
||||
<div class="article wrapper-lg">
|
||||
|
||||
<div class="article-thumbnail" ng-if="project.project_image">
|
||||
<a href="{{project.project_full_image}}" target="_blank"><img ng-src="{{project.project_image}}" alt="{{project.name}}"></a>
|
||||
</div>
|
||||
|
||||
<h3 translate>{{ 'app.public.projects_show.project_description' }}</h3>
|
||||
<p ng-bind-html="project.description | toTrusted"></p>
|
||||
|
||||
<div class="article-steps">
|
||||
<div class="row article-step m-b-lg" ng-repeat="step in project.project_steps_attributes">
|
||||
<div class="col-md-12 m-b-xs">
|
||||
<h3 class="well well-simple step-title">{{ 'app.public.projects_show.step_N' | translate:{INDEX:step.step_nb} }} : {{step.title}}</h3>
|
||||
</div>
|
||||
<div ng-repeat-start="image in step.project_step_images_attributes" class="clearfix" ng-if="$index % 3 == 0"></div>
|
||||
<div class="col-md-4" ng-repeat-end>
|
||||
<a href="{{image.attachment_full_url}}" target="_blank"><img class="m-b" ng-src="{{image.attachment_url}}" alt="{{image.attachment}}" ></a>
|
||||
</div>
|
||||
<div class="col-md-8" ng-class="{'col-md-12' : step.project_step_images_attributes.length > 1 || step.project_step_images_attributes.length == 0}">
|
||||
|
||||
<p ng-bind-html="step.description | toTrusted"></p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="text-center" id="social-share">
|
||||
<a ng-href="{{shareOnFacebook()}}" target="_blank" class="btn btn-facebook btn-lg m-t"><i class="fa fa-facebook m-r"></i> {{ 'app.public.projects_show.share_on_facebook' | translate }}</a>
|
||||
<a ng-href="{{shareOnTwitter()}}" target="_blank" class="btn btn-twitter btn-lg m-t"><i class="fa fa-twitter m-r"></i> {{ 'app.public.projects_show.share_on_twitter' | translate }}</a>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-lg" ng-if="disqusShortname">
|
||||
<dir-disqus disqus-shortname="{{ disqusShortname }}" disqus-identifier="project_{{ project.id }}" disqus-url="{{ projectUrl }}" ready-to-bind="{{ project }}">
|
||||
</dir-disqus>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="text-center" id="social-share">
|
||||
<a ng-href="{{shareOnFacebook()}}" target="_blank" class="btn btn-facebook btn-lg m-t"><i class="fa fa-facebook m-r"></i> {{ 'app.public.projects_show.share_on_facebook' | translate }}</a>
|
||||
<a ng-href="{{shareOnTwitter()}}" target="_blank" class="btn btn-twitter btn-lg m-t"><i class="fa fa-twitter m-r"></i> {{ 'app.public.projects_show.share_on_twitter' | translate }}</a>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-lg" ng-if="disqusShortname">
|
||||
<dir-disqus disqus-shortname="{{ disqusShortname }}" disqus-identifier="project_{{ project.id }}" disqus-url="{{ projectUrl }}" ready-to-bind="{{ project }}">
|
||||
</dir-disqus>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
|
||||
|
||||
<div class="text-center m-t-lg m-v">
|
||||
<div class="thumb-lg m-b-xs">
|
||||
<fab-user-avatar ng-model="project.author.user_avatar" avatar-class="thumb-50"></fab-user-avatar>
|
||||
<div class="text-center m-t-lg m-v">
|
||||
<div class="thumb-lg m-b-xs">
|
||||
<fab-user-avatar ng-model="project.author.user_avatar" avatar-class="thumb-50"></fab-user-avatar>
|
||||
</div>
|
||||
<div>
|
||||
<a ng-show="project.author_id" class="text-sm font-sbold project-author" ui-sref="app.logged.members_show({id: project.author.slug})">
|
||||
<i> {{ 'app.public.projects_show.by_name' | translate:{NAME:project.author.first_name} }}</i>
|
||||
</a>
|
||||
<span ng-hide="project.author_id" class="text-sm font-sbold text-gray" translate>{{ 'app.public.projects_show.deleted_user' }}</span>
|
||||
</div>
|
||||
<small class="text-xs m-b"><i>{{ 'app.public.projects_show.posted_on_' | translate }} {{project.created_at | amDateFormat: 'LL'}}</i></small>
|
||||
|
||||
<div class="m" ng-if="project.themes">
|
||||
<span ng-repeat="theme in project.themes" class="badge m-r-sm">
|
||||
{{theme.name}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<a ng-show="project.author_id" class="text-sm font-sbold project-author" ui-sref="app.logged.members_show({id: project.author.slug})">
|
||||
<i> {{ 'app.public.projects_show.by_name' | translate:{NAME:project.author.first_name} }}</i>
|
||||
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_caos_attributes">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.project_caos_attributes.length}}</span>
|
||||
<h3 translate translate-values="{COUNT:project.project_caos_attributes.length}">{{ 'app.public.projects_show.CAD_file_to_download' }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="file in project.project_caos_attributes" class="list-group-item no-b clearfix">
|
||||
<a target="_blank" ng-href="{{file.attachment_url}}" download="{{file.attachment_url}}"><i class="fa fa-arrow-circle-o-down"> </i> {{file.attachment | humanize : 25}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.status">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ 'app.public.projects_show.status' }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ project.status.name }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.machines">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.machines.length}}</span>
|
||||
<h3 translate>{{ 'app.public.projects_show.machines_and_materials' }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="machine in project.machines" class="list-group-item no-b clearfix">
|
||||
<a ui-sref="app.public.machines_show({id: machine.id})">{{machine.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="component in project.components" class="list-group-item no-b clearfix">
|
||||
{{component.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_users.length > 0">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.project_users.length}}</span>
|
||||
<h3 translate>{{ 'app.public.projects_show.collaborators' }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li class="list-group-item no-b clearfix block-link" ng-repeat="collaborator in project.project_users" ui-sref="app.logged.members_show({id: collaborator.slug})">
|
||||
<span class="pull-left thumb-sm avatar m-r">
|
||||
<fab-user-avatar ng-model="collaborator.user_avatar" avatar-class="thumb-38"></fab-user-avatar>
|
||||
|
||||
<i class="on b-white bottom" ng-if="collaborator.is_valid"></i>
|
||||
<i class="off b-white bottom" ng-if="!collaborator.is_valid"></i>
|
||||
</span>
|
||||
<span class="clear"><span>{{collaborator.full_name}}</span>
|
||||
<small class="text-muted clear text-ellipsis text-c">{{collaborator.username}}</small>
|
||||
</span>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.licence">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ 'app.public.projects_show.licence' }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ project.licence.name }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.tags">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ 'app.shared.project.tags' }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<pre>{{ project.tags }}</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_categories">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ projectCategoriesWording }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="projectCategory in project.project_categories" class="list-group-item no-b clearfix">
|
||||
{{projectCategory.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="text-center m m-b-lg" ng-if="projectEditableBy(currentUser) || isAuthorized('admin') || isAuthorized('manager')">
|
||||
<a class="btn bg-light text-black" ng-href="api/projects/{{ project.id}}/markdown" target="_blank">
|
||||
<i class="fa fa-download"></i> {{ 'app.public.projects_show.markdown_file' | translate }}
|
||||
</a>
|
||||
<span ng-hide="project.author_id" class="text-sm font-sbold text-gray" translate>{{ 'app.public.projects_show.deleted_user' }}</span>
|
||||
</div>
|
||||
<small class="text-xs m-b"><i>{{ 'app.public.projects_show.posted_on_' | translate }} {{project.created_at | amDateFormat: 'LL'}}</i></small>
|
||||
|
||||
<div class="m" ng-if="project.themes">
|
||||
<span ng-repeat="theme in project.themes" class="badge m-r-sm">
|
||||
{{theme.name}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_caos_attributes">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.project_caos_attributes.length}}</span>
|
||||
<h3 translate translate-values="{COUNT:project.project_caos_attributes.length}">{{ 'app.public.projects_show.CAD_file_to_download' }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="file in project.project_caos_attributes" class="list-group-item no-b clearfix">
|
||||
<a target="_blank" ng-href="{{file.attachment_url}}" download="{{file.attachment_url}}"><i class="fa fa-arrow-circle-o-down"> </i> {{file.attachment | humanize : 25}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.status">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ 'app.public.projects_show.status' }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ project.status.name }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.machines">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.machines.length}}</span>
|
||||
<h3 translate>{{ 'app.public.projects_show.machines_and_materials' }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="machine in project.machines" class="list-group-item no-b clearfix">
|
||||
<a ui-sref="app.public.machines_show({id: machine.id})">{{machine.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="component in project.components" class="list-group-item no-b clearfix">
|
||||
{{component.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_users.length > 0">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.project_users.length}}</span>
|
||||
<h3 translate>{{ 'app.public.projects_show.collaborators' }}</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li class="list-group-item no-b clearfix block-link" ng-repeat="collaborator in project.project_users" ui-sref="app.logged.members_show({id: collaborator.slug})">
|
||||
<span class="pull-left thumb-sm avatar m-r">
|
||||
<fab-user-avatar ng-model="collaborator.user_avatar" avatar-class="thumb-38"></fab-user-avatar>
|
||||
|
||||
<i class="on b-white bottom" ng-if="collaborator.is_valid"></i>
|
||||
<i class="off b-white bottom" ng-if="!collaborator.is_valid"></i>
|
||||
</span>
|
||||
<span class="clear"><span>{{collaborator.full_name}}</span>
|
||||
<small class="text-muted clear text-ellipsis text-c">{{collaborator.username}}</small>
|
||||
</span>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.licence">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ 'app.public.projects_show.licence' }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ project.licence.name }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.tags">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 translate>{{ 'app.shared.project.tags' }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<pre>{{ project.tags }}</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="widget b-t">
|
||||
|
||||
<div class="widget-content text-center m-t">
|
||||
<a ng-click="signalAbuse($event)"><i class="fa fa-warning"></i> {{ 'app.public.projects_show.report_an_abuse' | translate }}</a>
|
||||
</div>
|
||||
<div class="widget-content text-center m-t">
|
||||
<a ng-click="signalAbuse($event)"><i class="fa fa-warning"></i> {{ 'app.public.projects_show.report_an_abuse' | translate }}</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -49,7 +49,7 @@
|
||||
<li><a ui-sref="app.logged.dashboard.events" translate>{{ 'app.public.common.my_events' }}</a></li>
|
||||
<li><a ui-sref="app.logged.dashboard.invoices" ng-show="$root.modules.invoicing" translate>{{ 'app.public.common.my_invoices' }}</a></li>
|
||||
<li><a ui-sref="app.logged.dashboard.payment_schedules" ng-show="$root.modules.invoicing" translate>{{ 'app.public.common.my_payment_schedules' }}</a></li>
|
||||
<li><a ui-sref="app.logged.dashboard.orders" translate>{{ 'app.public.common.my_orders' }}</a></li>
|
||||
<li ng-if="$root.modules.store"><a ui-sref="app.logged.dashboard.orders" translate>{{ 'app.public.common.my_orders' }}</a></li>
|
||||
<li ng-show="$root.modules.wallet"><a ui-sref="app.logged.dashboard.wallet" translate>{{ 'app.public.common.my_wallet' }}</a></li>
|
||||
<li class="divider" ng-if="isAuthorized(['admin', 'manager'])"></li>
|
||||
<li><a class="text-black pointer" ng-click="help($event)" ng-if="isAuthorized(['admin', 'manager'])"><i class="fa fa-question-circle"></i> <span translate>{{ 'app.public.common.help' }}</span> </a></li>
|
||||
|
@ -198,6 +198,10 @@ module SettingsHelper
|
||||
events_banner_cta_active
|
||||
events_banner_cta_label
|
||||
events_banner_cta_url
|
||||
projects_list_member_filter_presence
|
||||
projects_list_date_filters_presence
|
||||
project_categories_filter_placeholder
|
||||
project_categories_wording
|
||||
].freeze
|
||||
end
|
||||
# rubocop:enable Metrics/ModuleLength
|
||||
|
@ -20,7 +20,7 @@ class Footprintable < ApplicationRecord
|
||||
return false unless persisted?
|
||||
|
||||
reload
|
||||
footprint_children.map(&:check_footprint).all? && !chained_element.corrupted?
|
||||
footprint_children.map(&:check_footprint).all? && chained_element && !chained_element.corrupted?
|
||||
end
|
||||
|
||||
# @return [ChainedElement]
|
||||
|
@ -175,8 +175,8 @@ class Invoice < PaymentDocument
|
||||
if paid_by_card?
|
||||
{
|
||||
payment_mean: mean,
|
||||
gateway_object_id: payment_gateway_object.gateway_object_id,
|
||||
gateway_object_type: payment_gateway_object.gateway_object_type
|
||||
gateway_object_id: payment_gateway_object&.gateway_object_id,
|
||||
gateway_object_type: payment_gateway_object&.gateway_object_type
|
||||
}
|
||||
end
|
||||
when :wallet
|
||||
|
@ -41,6 +41,8 @@ class Project < ApplicationRecord
|
||||
accepts_nested_attributes_for :project_steps, allow_destroy: true
|
||||
|
||||
has_many :abuses, as: :signaled, dependent: :destroy, class_name: 'Abuse'
|
||||
has_many :projects_project_categories, dependent: :destroy
|
||||
has_many :project_categories, through: :projects_project_categories
|
||||
|
||||
# validations
|
||||
validates :author, :name, presence: true
|
||||
@ -68,6 +70,7 @@ class Project < ApplicationRecord
|
||||
scope :with_component, ->(component_ids) { joins(:projects_components).where(projects_components: { component_id: component_ids }) }
|
||||
scope :with_space, ->(spaces_ids) { joins(:projects_spaces).where(projects_spaces: { space_id: spaces_ids }) }
|
||||
scope :with_status, ->(statuses_ids) { where(status_id: statuses_ids) }
|
||||
scope :with_project_category, ->(project_category_ids) { joins(:projects_project_categories).where(projects_project_categories: { project_category_id: project_category_ids }) }
|
||||
pg_search_scope :search,
|
||||
against: :search_vector,
|
||||
using: {
|
||||
|
6
app/models/project_category.rb
Normal file
6
app/models/project_category.rb
Normal file
@ -0,0 +1,6 @@
|
||||
class ProjectCategory < ApplicationRecord
|
||||
validates :name, presence: true
|
||||
|
||||
has_many :projects_project_categories, dependent: :destroy
|
||||
has_many :projects, through: :projects_project_categories
|
||||
end
|
@ -5,4 +5,6 @@ class ProjectStep < ApplicationRecord
|
||||
belongs_to :project, touch: true
|
||||
has_many :project_step_images, as: :viewable, dependent: :destroy
|
||||
accepts_nested_attributes_for :project_step_images, allow_destroy: true, reject_if: :all_blank
|
||||
|
||||
default_scope -> { order(:step_nb) }
|
||||
end
|
||||
|
4
app/models/projects_project_category.rb
Normal file
4
app/models/projects_project_category.rb
Normal file
@ -0,0 +1,4 @@
|
||||
class ProjectsProjectCategory < ApplicationRecord
|
||||
belongs_to :project
|
||||
belongs_to :project_category
|
||||
end
|
16
app/policies/project_category_policy.rb
Normal file
16
app/policies/project_category_policy.rb
Normal file
@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check if user is an admin to allow create, update and destroy project_category
|
||||
class ProjectCategoryPolicy < ApplicationPolicy
|
||||
def create?
|
||||
user.admin?
|
||||
end
|
||||
|
||||
def update?
|
||||
create?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
create?
|
||||
end
|
||||
end
|
@ -16,10 +16,14 @@ class ProjectPolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def update?
|
||||
user.admin? or record.author.user_id == user.id or record.users.include?(user)
|
||||
user.admin? || record.author.user_id == user.id || record.users.include?(user)
|
||||
end
|
||||
|
||||
def markdown?
|
||||
user.admin? || user.manager? || record.author.user_id == user.id || record.users.include?(user)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.admin? or record.author.user_id == user.id
|
||||
user.admin? || record.author.user_id == user.id
|
||||
end
|
||||
end
|
||||
|
@ -46,7 +46,8 @@ class SettingPolicy < ApplicationPolicy
|
||||
external_id machines_banner_active machines_banner_text machines_banner_cta_active machines_banner_cta_label
|
||||
machines_banner_cta_url trainings_banner_active trainings_banner_text trainings_banner_cta_active trainings_banner_cta_label
|
||||
trainings_banner_cta_url events_banner_active events_banner_text events_banner_cta_active events_banner_cta_label
|
||||
events_banner_cta_url family_account child_validation_required]
|
||||
events_banner_cta_url projects_list_member_filter_presence projects_list_date_filters_presence
|
||||
project_categories_filter_placeholder project_categories_wording family_account child_validation_required]
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -22,11 +22,19 @@ class Accounting::AccountingService
|
||||
lines = []
|
||||
processed = []
|
||||
invoices.find_each do |i|
|
||||
Rails.logger.debug { "processing invoice #{i.id}..." } unless Rails.env.test?
|
||||
lines.concat(generate_lines(i))
|
||||
processed.push(i.id)
|
||||
Rails.logger.debug { "[AccountLine] processing invoice #{i.id}..." } unless Rails.env.test?
|
||||
if i.main_item.nil?
|
||||
Rails.logger.error { "[AccountLine] invoice #{i.id} main_item is nil" } unless Rails.env.test?
|
||||
else
|
||||
lines.concat(generate_lines(i))
|
||||
processed.push(i.id)
|
||||
end
|
||||
end
|
||||
ActiveRecord::Base.transaction do
|
||||
ids = invoices.map(&:id)
|
||||
AccountingLine.where(invoice_id: ids).delete_all
|
||||
AccountingLine.create!(lines)
|
||||
end
|
||||
AccountingLine.create!(lines)
|
||||
processed
|
||||
end
|
||||
|
||||
|
@ -2,17 +2,17 @@
|
||||
|
||||
# Provides methods for Project
|
||||
class ProjectService
|
||||
def search(params, current_user)
|
||||
def search(params, current_user, paginate: true)
|
||||
connection = ActiveRecord::Base.connection
|
||||
return { error: 'invalid adapter' } unless connection.instance_values['config'][:adapter] == 'postgresql'
|
||||
|
||||
search_from_postgre(params, current_user)
|
||||
search_from_postgre(params, current_user, paginate: paginate)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_from_postgre(params, current_user)
|
||||
query_params = JSON.parse(params[:search])
|
||||
def search_from_postgre(params, current_user, paginate: true)
|
||||
query_params = JSON.parse(params[:search] || "{}")
|
||||
|
||||
records = Project.published_or_drafts(current_user&.statistic_profile&.id)
|
||||
records = Project.user_projects(current_user&.statistic_profile&.id) if query_params['from'] == 'mine'
|
||||
@ -21,14 +21,32 @@ class ProjectService
|
||||
records = records.with_machine(query_params['machine_id']) if query_params['machine_id'].present?
|
||||
records = records.with_component(query_params['component_id']) if query_params['component_id'].present?
|
||||
records = records.with_theme(query_params['theme_id']) if query_params['theme_id'].present?
|
||||
records = records.with_project_category(query_params['project_category_id']) if query_params['project_category_id'].present?
|
||||
records = records.with_space(query_params['space_id']) if query_params['space_id'].present?
|
||||
records = records.with_status(query_params['status_id']) if query_params['status_id'].present?
|
||||
|
||||
if query_params['member_id'].present?
|
||||
member = User.find(query_params['member_id'])
|
||||
if member
|
||||
records = records.where(id: Project.user_projects(member.statistic_profile.id)).or(Project.where(id: Project.collaborations(member.id)))
|
||||
end
|
||||
end
|
||||
|
||||
created_from = Time.zone.parse(query_params['from_date']).beginning_of_day if query_params['from_date'].present?
|
||||
created_to = Time.zone.parse(query_params['to_date']).end_of_day if query_params['to_date'].present?
|
||||
if created_from || created_to
|
||||
records = records.where(created_at: created_from..created_to)
|
||||
end
|
||||
|
||||
records = if query_params['q'].present?
|
||||
records.search(query_params['q'])
|
||||
else
|
||||
records.order(created_at: :desc)
|
||||
end
|
||||
|
||||
{ total: records.count, projects: records.includes(:users, :project_image).page(params[:page]) }
|
||||
records = records.includes(:users, :project_image)
|
||||
records = records.page(params[:page]) if paginate
|
||||
|
||||
{ total: records.count, projects: records }
|
||||
end
|
||||
end
|
||||
|
88
app/services/project_to_markdown.rb
Normal file
88
app/services/project_to_markdown.rb
Normal file
@ -0,0 +1,88 @@
|
||||
class ProjectToMarkdown
|
||||
attr_reader :project
|
||||
|
||||
def initialize(project)
|
||||
@project = project
|
||||
end
|
||||
|
||||
def call
|
||||
md = []
|
||||
|
||||
md << "# #{project.name}"
|
||||
|
||||
md << "![#{I18n.t('app.shared.project.illustration')}](#{full_url(project.project_image.attachment.url)})" if project.project_image
|
||||
|
||||
md << ReverseMarkdown.convert(project.description.to_s)
|
||||
|
||||
project_steps = project.project_steps
|
||||
|
||||
if project_steps.present?
|
||||
md << "## #{I18n.t('app.shared.project.steps')}"
|
||||
|
||||
project_steps.each do |project_step|
|
||||
md << "### #{I18n.t('app.shared.project.step_N').gsub('{INDEX}', project_step.step_nb.to_s)} : #{project_step.title}"
|
||||
md << ReverseMarkdown.convert(project_step.description.to_s)
|
||||
|
||||
project_step.project_step_images.each_with_index do |image, i|
|
||||
md << "![#{I18n.t('app.shared.project.step_image')} #{i+1}](#{full_url(project.project_image.attachment.url)})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
md << "## #{I18n.t('app.shared.project.author')}"
|
||||
md << project.author&.user&.profile&.full_name
|
||||
|
||||
if project.themes.present?
|
||||
md << "## #{I18n.t('app.shared.project.themes')}"
|
||||
md << project.themes.map(&:name).join(', ')
|
||||
end
|
||||
|
||||
if project.project_caos.present?
|
||||
md << "## #{I18n.t('app.shared.project.CAD_files')}"
|
||||
project.project_caos.each do |cao|
|
||||
md << "![#{cao.attachment_identifier}](#{full_url(cao.attachment_url)})"
|
||||
end
|
||||
end
|
||||
|
||||
if project.status
|
||||
md << "## #{I18n.t('app.shared.project.status')}"
|
||||
md << project.status.name
|
||||
end
|
||||
|
||||
if project.machines.present?
|
||||
md << "## #{I18n.t('app.shared.project.employed_machines')}"
|
||||
md << project.machines.map(&:name).join(', ')
|
||||
end
|
||||
|
||||
if project.components.present?
|
||||
md << "## #{I18n.t('app.shared.project.employed_materials')}"
|
||||
md << project.components.map(&:name).join(', ')
|
||||
end
|
||||
|
||||
if project.users.present?
|
||||
md << "## #{I18n.t('app.shared.project.collaborators')}"
|
||||
md << project.users.map { |u| u.profile.full_name }.join(', ')
|
||||
end
|
||||
|
||||
if project.licence.present?
|
||||
md << "## #{I18n.t('app.shared.project.licence')}"
|
||||
md << project.licence.name
|
||||
end
|
||||
|
||||
if project.tags.present?
|
||||
md << "## #{I18n.t('app.shared.project.tags')}"
|
||||
md << project.tags
|
||||
end
|
||||
|
||||
|
||||
md = md.reject { |line| line.blank? }
|
||||
|
||||
md.join("\n\n")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def full_url(path)
|
||||
"#{Rails.application.routes.url_helpers.root_url[...-1]}#{path}"
|
||||
end
|
||||
end
|
22
app/services/projects_archive.rb
Normal file
22
app/services/projects_archive.rb
Normal file
@ -0,0 +1,22 @@
|
||||
class ProjectsArchive
|
||||
attr_reader :projects
|
||||
|
||||
def initialize(projects)
|
||||
@projects = projects
|
||||
end
|
||||
|
||||
def call
|
||||
stringio = Zip::OutputStream.write_buffer do |zio|
|
||||
projects.includes(:project_image, :themes,
|
||||
:project_caos, :status, :machines,
|
||||
:components, :licence,
|
||||
project_steps: :project_step_images,
|
||||
author: { user: :profile },
|
||||
users: :profile).find_each do |project|
|
||||
zio.put_next_entry("#{project.name.parameterize}-#{project.id}.md")
|
||||
zio.write ProjectToMarkdown.new(project).call
|
||||
end
|
||||
end
|
||||
stringio.string
|
||||
end
|
||||
end
|
@ -14,8 +14,8 @@ if payment_schedule.operator_profile
|
||||
end
|
||||
end
|
||||
json.main_object do
|
||||
json.type payment_schedule.main_object.object_type
|
||||
json.id payment_schedule.main_object.object_id
|
||||
json.type payment_schedule.main_object&.object_type
|
||||
json.id payment_schedule.main_object&.object_id
|
||||
end
|
||||
if payment_schedule.gateway_subscription
|
||||
# this attribute is used to known which gateway should we interact with, in the front-end
|
||||
|
5
app/views/api/project_categories/index.json.jbuilder
Normal file
5
app/views/api/project_categories/index.json.jbuilder
Normal file
@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.array!(@project_categories) do |project_category|
|
||||
json.extract! project_category, :id, :name
|
||||
end
|
@ -7,6 +7,7 @@ json.user_ids project.user_ids
|
||||
json.machine_ids project.machine_ids
|
||||
json.theme_ids project.theme_ids
|
||||
json.component_ids project.component_ids
|
||||
json.project_category_ids project.project_category_ids
|
||||
json.tags project.tags
|
||||
json.name project.name
|
||||
json.description project.description
|
||||
|
@ -39,6 +39,11 @@ json.themes @project.themes do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
json.project_category_ids @project.project_category_ids
|
||||
json.project_categories @project.project_categories do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
json.user_ids @project.user_ids
|
||||
json.project_users @project.project_users do |pu|
|
||||
json.id pu.user.id
|
||||
|
@ -27,8 +27,6 @@ class AccountingWorker
|
||||
end
|
||||
|
||||
def invoices(invoices_ids)
|
||||
# clean
|
||||
AccountingLine.where(invoice_id: invoices_ids).delete_all
|
||||
# build
|
||||
service = Accounting::AccountingService.new
|
||||
invoices = Invoice.where(id: invoices_ids)
|
||||
@ -37,8 +35,6 @@ class AccountingWorker
|
||||
end
|
||||
|
||||
def all
|
||||
# clean
|
||||
AccountingLine.delete_all
|
||||
# build
|
||||
service = Accounting::AccountingService.new
|
||||
ids = service.build_from_invoices(Invoice.all)
|
||||
|
@ -415,6 +415,8 @@ de:
|
||||
add_a_material: "Materialien hinfügen"
|
||||
themes: "Themen"
|
||||
add_a_new_theme: "Neues Thema hinzufügen"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Lizenzen"
|
||||
statuses: "Statuses"
|
||||
description: "Beschreibung"
|
||||
@ -445,6 +447,10 @@ de:
|
||||
open_lab_app_secret: "Geheimnis"
|
||||
openlab_default_info_html: "In der Projektgalerie können Besucher zwischen zwei Ansichten wechseln: alle gemeinsam geteilten Projekte des OpenLab-Netzwerkes oder nur die in Ihrem FabLab dokumentierten Projekte.<br/>Hier können Sie die standardmäßig angezeigte Ansicht auswählen."
|
||||
default_to_openlab: "OpenLab standardmäßig anzeigen"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Hinzufügen"
|
||||
actions_controls: "Actions"
|
||||
@ -1773,6 +1779,10 @@ de:
|
||||
extended_prices_in_same_day: "Erweiterte Preise am selben Tag"
|
||||
public_registrations: "Öffentliche Registrierungen"
|
||||
show_username_in_admin_list: "Show the username in the list"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
overlapping_options:
|
||||
training_reservations: "Schulungen"
|
||||
machine_reservations: "Maschinen"
|
||||
|
@ -423,6 +423,8 @@ en:
|
||||
add_a_material: "Add a material"
|
||||
themes: "Themes"
|
||||
add_a_new_theme: "Add a new theme"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Licences"
|
||||
statuses: "Statuses"
|
||||
description: "Description"
|
||||
@ -453,6 +455,10 @@ en:
|
||||
open_lab_app_secret: "Secret"
|
||||
openlab_default_info_html: "In the projects gallery, visitors can switch between two views: all shared projects from the whole OpenLab network, or only the projects documented in your Fab Lab.<br/>Here, you can choose which view is shown by default."
|
||||
default_to_openlab: "Display OpenLab by default"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Add"
|
||||
actions_controls: "Actions"
|
||||
@ -1823,6 +1829,10 @@ en:
|
||||
extended_prices_in_same_day: "Extended prices in the same day"
|
||||
public_registrations: "Public registrations"
|
||||
show_username_in_admin_list: "Show the username in the list"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
family_account: "family account"
|
||||
family_account_info_html: "The Family account allows your members to add their children under 18 years old to their own account and directly register them for Family events. You can also request supporting documents for each child and validate their account."
|
||||
enable_family_account: "Enable the Family Account option"
|
||||
|
@ -415,6 +415,8 @@ es:
|
||||
add_a_material: "Añadir un material"
|
||||
themes: "Temas"
|
||||
add_a_new_theme: "Añadir un nuevo tema"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Licencias"
|
||||
statuses: "Statuses"
|
||||
description: "Descripción"
|
||||
@ -445,6 +447,10 @@ es:
|
||||
open_lab_app_secret: "Secret"
|
||||
openlab_default_info_html: "In the projects gallery, visitors can switch between two views: all shared projects from the whole OpenLab network, or only the projects documented in your Fab Lab.<br/>Here, you can choose which view is shown by default."
|
||||
default_to_openlab: "Display OpenLab by default"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Add"
|
||||
actions_controls: "Actions"
|
||||
@ -1773,6 +1779,10 @@ es:
|
||||
extended_prices_in_same_day: "Extended prices in the same day"
|
||||
public_registrations: "Public registrations"
|
||||
show_username_in_admin_list: "Show the username in the list"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
overlapping_options:
|
||||
training_reservations: "Trainings"
|
||||
machine_reservations: "Machines"
|
||||
|
@ -423,6 +423,8 @@ fr:
|
||||
add_a_material: "Ajouter un matériau"
|
||||
themes: "Thématiques"
|
||||
add_a_new_theme: "Ajouter une nouvelle thématique"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Licences"
|
||||
statuses: "Statuts"
|
||||
description: "Description"
|
||||
@ -453,6 +455,10 @@ fr:
|
||||
open_lab_app_secret: "Secret"
|
||||
openlab_default_info_html: "Dans la galerie de projets, les visiteurs peuvent choisir entre deux vues : tous les projets de l'ensemble du réseau OpenLab, ou uniquement les projets documentés dans votre Fab Lab.<br/>Ici, vous pouvez choisir quelle vue est affichée par défaut."
|
||||
default_to_openlab: "Afficher OpenLab par défaut"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Ajouter"
|
||||
actions_controls: "Actions"
|
||||
@ -1815,6 +1821,10 @@ fr:
|
||||
extended_prices_in_same_day: "Prix étendus le même jour"
|
||||
public_registrations: "Inscriptions publiques"
|
||||
show_username_in_admin_list: "Afficher le nom d'utilisateur dans la liste"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
family_account: "Compte famille"
|
||||
family_account_info_html: "Le compte Famille permet à vos membres d'ajouter leurs enfants de moins de 18 ans sur leur propre compte et de les inscrire directement aux évènements de type Famille. Vous pouvez aussi demander des justificatifs pour chaque enfant et valider leur compte."
|
||||
enable_family_account: "Activer l'option Compte Famille"
|
||||
|
@ -415,6 +415,8 @@ it:
|
||||
add_a_material: "Aggiungi un materiale"
|
||||
themes: "Temi"
|
||||
add_a_new_theme: "Aggiungi un nuovo tema"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Licenze"
|
||||
statuses: "Status"
|
||||
description: "Descrizione"
|
||||
@ -445,6 +447,10 @@ it:
|
||||
open_lab_app_secret: "Segreto"
|
||||
openlab_default_info_html: "Nella galleria di progetti, i visitatori possono scegliere tra due viste: tutti i progetti condivisi da tutta la rete di OpenLab, o solo i progetti documentati nel tuo Fab Lab.<br/>Qui, puoi scegliere quale vista è mostrata per impostazione predefinita."
|
||||
default_to_openlab: "Visualizza OpenLab per impostazione predefinita"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Aggiungi"
|
||||
actions_controls: "Azioni"
|
||||
@ -1773,6 +1779,10 @@ it:
|
||||
extended_prices_in_same_day: "Prezzi estesi nello stesso giorno"
|
||||
public_registrations: "Registri pubblici"
|
||||
show_username_in_admin_list: "Mostra il nome utente nella lista"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
overlapping_options:
|
||||
training_reservations: "Abilitazioni"
|
||||
machine_reservations: "Macchine"
|
||||
|
@ -415,6 +415,8 @@
|
||||
add_a_material: "Legg til et materiale"
|
||||
themes: "Temaer"
|
||||
add_a_new_theme: "Legge til et nytt tema"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Lisenser"
|
||||
statuses: "Statuses"
|
||||
description: "Beskrivelse"
|
||||
@ -445,6 +447,10 @@
|
||||
open_lab_app_secret: "Hemmelighet"
|
||||
openlab_default_info_html: "I prosjektgalleriet kan besøkende bytte mellom to visninger: alle delte projetter fra hele OpenLab-nettverket. eller bare prosjektene som er dokumentert i din Fab Lab.<br/>Her kan du velge hvilken visning som standard."
|
||||
default_to_openlab: "Vis OpenLab som standard"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Add"
|
||||
actions_controls: "Actions"
|
||||
@ -1773,6 +1779,10 @@
|
||||
extended_prices_in_same_day: "Extended prices in the same day"
|
||||
public_registrations: "Public registrations"
|
||||
show_username_in_admin_list: "Show the username in the list"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
overlapping_options:
|
||||
training_reservations: "Trainings"
|
||||
machine_reservations: "Machines"
|
||||
|
@ -415,6 +415,8 @@ pt:
|
||||
add_a_material: "Adicionar material"
|
||||
themes: "Temas"
|
||||
add_a_new_theme: "Adicionar um novo tema"
|
||||
project_categories: "Categories"
|
||||
add_a_new_project_category: "Add a new category"
|
||||
licences: "Licenças"
|
||||
statuses: "Statuses"
|
||||
description: "Descrição"
|
||||
@ -445,6 +447,10 @@ pt:
|
||||
open_lab_app_secret: "Senha"
|
||||
openlab_default_info_html: "Na galeria de projetos, os visitantes podem alternar entre duas visualizações: todos os projetos compartilhados de toda a rede OpenLab, ou apenas os projetos documentados no seu Fab Lab.<br/>Aqui, você pode escolher qual exibição é mostrada por padrão."
|
||||
default_to_openlab: "Mostrar OpenLab por padrão"
|
||||
filters: Projects list filters
|
||||
project_categories: Categories
|
||||
project_categories:
|
||||
name: "Name"
|
||||
projects_setting:
|
||||
add: "Add"
|
||||
actions_controls: "Actions"
|
||||
@ -1773,6 +1779,10 @@ pt:
|
||||
extended_prices_in_same_day: "Preços estendidos no mesmo dia"
|
||||
public_registrations: "Inscrições públicas"
|
||||
show_username_in_admin_list: "Mostrar o nome de usuário na lista"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of date filters on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
overlapping_options:
|
||||
training_reservations: "Treinamentos"
|
||||
machine_reservations: "Máquinas"
|
||||
|
@ -415,6 +415,8 @@ zu:
|
||||
add_a_material: "crwdns24306:0crwdne24306:0"
|
||||
themes: "crwdns24308:0crwdne24308:0"
|
||||
add_a_new_theme: "crwdns24310:0crwdne24310:0"
|
||||
project_categories: "crwdns37617:0crwdne37617:0"
|
||||
add_a_new_project_category: "crwdns37619:0crwdne37619:0"
|
||||
licences: "crwdns24312:0crwdne24312:0"
|
||||
statuses: "crwdns36893:0crwdne36893:0"
|
||||
description: "crwdns24314:0crwdne24314:0"
|
||||
@ -445,6 +447,10 @@ zu:
|
||||
open_lab_app_secret: "crwdns24362:0crwdne24362:0"
|
||||
openlab_default_info_html: "crwdns37609:0crwdne37609:0"
|
||||
default_to_openlab: "crwdns24366:0crwdne24366:0"
|
||||
filters: crwdns37621:0crwdne37621:0
|
||||
project_categories: crwdns37623:0crwdne37623:0
|
||||
project_categories:
|
||||
name: "crwdns37625:0crwdne37625:0"
|
||||
projects_setting:
|
||||
add: "crwdns36895:0crwdne36895:0"
|
||||
actions_controls: "crwdns36897:0crwdne36897:0"
|
||||
@ -1773,6 +1779,10 @@ zu:
|
||||
extended_prices_in_same_day: "crwdns26752:0crwdne26752:0"
|
||||
public_registrations: "crwdns26754:0crwdne26754:0"
|
||||
show_username_in_admin_list: "crwdns26756:0crwdne26756:0"
|
||||
projects_list_member_filter_presence: "crwdns37627:0crwdne37627:0"
|
||||
projects_list_date_filters_presence: "crwdns37629:0crwdne37629:0"
|
||||
project_categories_filter_placeholder: "crwdns37631:0crwdne37631:0"
|
||||
project_categories_wording: "crwdns37633:0crwdne37633:0"
|
||||
overlapping_options:
|
||||
training_reservations: "crwdns26758:0crwdne26758:0"
|
||||
machine_reservations: "crwdns26760:0crwdne26760:0"
|
||||
|
@ -167,6 +167,7 @@ de:
|
||||
full_price: "Voller Preis: "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "The projects"
|
||||
add_a_project: "Projekt hinzufügen"
|
||||
network_search: "Fab-manager network"
|
||||
@ -183,6 +184,10 @@ de:
|
||||
all_materials: "Alle Materialien"
|
||||
load_next_projects: "Nächste Projekte laden"
|
||||
rough_draft: "Grober Entwurf"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "All statuses"
|
||||
select_status: "Select a status"
|
||||
@ -216,6 +221,7 @@ de:
|
||||
report: "Melden"
|
||||
do_you_really_want_to_delete_this_project: "Wollen Sie dieses Projekt wirklich löschen?"
|
||||
status: "Status"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "The machines"
|
||||
|
@ -168,6 +168,7 @@ en:
|
||||
full_price: "Full price: "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "The projects"
|
||||
add_a_project: "Add a project"
|
||||
network_search: "Fab-manager network"
|
||||
@ -184,6 +185,10 @@ en:
|
||||
all_materials: "All materials"
|
||||
load_next_projects: "Load next projects"
|
||||
rough_draft: "Rough draft"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "All statuses"
|
||||
select_status: "Select a status"
|
||||
@ -217,6 +222,7 @@ en:
|
||||
report: "Report"
|
||||
do_you_really_want_to_delete_this_project: "Do you really want to delete this project?"
|
||||
status: "Status"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "The machines"
|
||||
|
@ -167,6 +167,7 @@ es:
|
||||
full_price: "Full price: "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "The projects"
|
||||
add_a_project: "Añadir un proyecto"
|
||||
network_search: "Fab-manager network"
|
||||
@ -183,6 +184,10 @@ es:
|
||||
all_materials: "Todo el material"
|
||||
load_next_projects: "Cargar más proyectos"
|
||||
rough_draft: "Borrador"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "All statuses"
|
||||
select_status: "Select a status"
|
||||
@ -216,6 +221,7 @@ es:
|
||||
report: "Reportar"
|
||||
do_you_really_want_to_delete_this_project: "¿Está seguro de querer eliminar este proyecto?"
|
||||
status: "Status"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "The machines"
|
||||
|
@ -168,6 +168,7 @@ fr:
|
||||
full_price: "Plein tarif : "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "Les projets"
|
||||
add_a_project: "Ajouter un projet"
|
||||
network_search: "Réseau Fab-Manager"
|
||||
@ -184,6 +185,10 @@ fr:
|
||||
all_materials: "Tous les matériaux"
|
||||
load_next_projects: "Charger les projets suivants"
|
||||
rough_draft: "Brouillon"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "Tous les statuts"
|
||||
select_status: "Sélectionnez un statut"
|
||||
@ -217,6 +222,7 @@ fr:
|
||||
report: "Signaler"
|
||||
do_you_really_want_to_delete_this_project: "Êtes-vous sur de vouloir supprimer ce projet ?"
|
||||
status: "Statut"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "Les machines"
|
||||
@ -366,7 +372,7 @@ fr:
|
||||
last_name_and_first_name: "Nom et prénom"
|
||||
pre_book: "Pré-inscrire"
|
||||
pre_registration_end_date: "Date limite de pré-inscription"
|
||||
pre_registration: "Pré-réservation"
|
||||
pre_registration: "Pré-inscription"
|
||||
#public calendar
|
||||
calendar:
|
||||
calendar: "Calendrier"
|
||||
|
@ -167,6 +167,7 @@ it:
|
||||
full_price: "Prezzo intero: "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "Progetti"
|
||||
add_a_project: "Aggiungi un progetto"
|
||||
network_search: "Fab-manager network"
|
||||
@ -183,6 +184,10 @@ it:
|
||||
all_materials: "Tutti i materiali"
|
||||
load_next_projects: "Carica i progetti successivi"
|
||||
rough_draft: "Bozza preliminare"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "Tutti gli stati"
|
||||
select_status: "Seleziona uno status"
|
||||
@ -216,6 +221,7 @@ it:
|
||||
report: "Segnalazione"
|
||||
do_you_really_want_to_delete_this_project: "Vuoi davvero eliminare questo progetto?"
|
||||
status: "Stato"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "Le macchine"
|
||||
|
@ -167,6 +167,7 @@
|
||||
full_price: "Full pris: "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "The projects"
|
||||
add_a_project: "Legg til et prosjekt"
|
||||
network_search: "Fab-manager network"
|
||||
@ -183,6 +184,10 @@
|
||||
all_materials: "Alle materialer"
|
||||
load_next_projects: "Last neste prosjekt"
|
||||
rough_draft: "Tidlig utkast"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "All statuses"
|
||||
select_status: "Select a status"
|
||||
@ -216,6 +221,7 @@
|
||||
report: "Rapporter"
|
||||
do_you_really_want_to_delete_this_project: "Vil du virkelig slette dette prosjektet?"
|
||||
status: "Status"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "The machines"
|
||||
|
@ -167,6 +167,7 @@ pt:
|
||||
full_price: "Valor inteira: "
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: Filter
|
||||
the_fablab_projects: "Os projetos"
|
||||
add_a_project: "Adicionar projeto"
|
||||
network_search: "Rede Fab-manager"
|
||||
@ -183,6 +184,10 @@ pt:
|
||||
all_materials: "Todos os materiais"
|
||||
load_next_projects: "Carregar próximos projetos"
|
||||
rough_draft: "Rascunho"
|
||||
filter_by_member: "Filter by member"
|
||||
created_from: Created from
|
||||
created_to: Created to
|
||||
download_archive: Download
|
||||
status_filter:
|
||||
all_statuses: "Todos os status"
|
||||
select_status: "Selecione um status"
|
||||
@ -216,6 +221,7 @@ pt:
|
||||
report: "Enviar"
|
||||
do_you_really_want_to_delete_this_project: "Você quer realmente deletar esse projeto?"
|
||||
status: "Status"
|
||||
markdown_file: "Markdown file"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "As máquinas"
|
||||
|
@ -167,6 +167,7 @@ zu:
|
||||
full_price: "crwdns28058:0crwdne28058:0"
|
||||
#projects gallery
|
||||
projects_list:
|
||||
filter: crwdns37635:0crwdne37635:0
|
||||
the_fablab_projects: "crwdns36237:0crwdne36237:0"
|
||||
add_a_project: "crwdns28062:0crwdne28062:0"
|
||||
network_search: "crwdns37071:0crwdne37071:0"
|
||||
@ -183,6 +184,10 @@ zu:
|
||||
all_materials: "crwdns28088:0crwdne28088:0"
|
||||
load_next_projects: "crwdns28090:0crwdne28090:0"
|
||||
rough_draft: "crwdns28092:0crwdne28092:0"
|
||||
filter_by_member: "crwdns37637:0crwdne37637:0"
|
||||
created_from: crwdns37639:0crwdne37639:0
|
||||
created_to: crwdns37641:0crwdne37641:0
|
||||
download_archive: crwdns37643:0crwdne37643:0
|
||||
status_filter:
|
||||
all_statuses: "crwdns37073:0crwdne37073:0"
|
||||
select_status: "crwdns37075:0crwdne37075:0"
|
||||
@ -216,6 +221,7 @@ zu:
|
||||
report: "crwdns28144:0crwdne28144:0"
|
||||
do_you_really_want_to_delete_this_project: "crwdns28146:0crwdne28146:0"
|
||||
status: "crwdns37077:0crwdne37077:0"
|
||||
markdown_file: "crwdns37645:0crwdne37645:0"
|
||||
#list of machines
|
||||
machines_list:
|
||||
the_fablab_s_machines: "crwdns36239:0crwdne36239:0"
|
||||
|
@ -131,6 +131,7 @@ de:
|
||||
illustration: "Ansicht"
|
||||
add_an_illustration: "Illustration hinzufügen"
|
||||
CAD_file: "CAD-Datei"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Zugelassene Dateitypen:"
|
||||
add_a_new_file: "Neue Datei hinzufügen"
|
||||
description: "Beschreibung"
|
||||
@ -138,6 +139,7 @@ de:
|
||||
steps: "Schritte"
|
||||
step_N: "Schritt {INDEX}"
|
||||
step_title: "Titel des Schrits"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Ein Bild hinzufügen"
|
||||
change_the_picture: "Bild ändern"
|
||||
delete_the_step: "Diesen Schritt löschen"
|
||||
@ -149,7 +151,9 @@ de:
|
||||
employed_materials: "Verwendetes Material"
|
||||
employed_machines: "Verwendete Maschinen"
|
||||
collaborators: "Mitarbeitende"
|
||||
author: Author
|
||||
creative_commons_licences: "Creative Commons-Lizenzen"
|
||||
licence: "Licence"
|
||||
themes: "Themen"
|
||||
tags: "Stichwörter"
|
||||
save_as_draft: "Als Entwurf speichern"
|
||||
|
@ -131,6 +131,7 @@ en:
|
||||
illustration: "Visual"
|
||||
add_an_illustration: "Add an illustration"
|
||||
CAD_file: "CAD file"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Allowed extensions:"
|
||||
add_a_new_file: "Add a new file"
|
||||
description: "Description"
|
||||
@ -138,6 +139,7 @@ en:
|
||||
steps: "Steps"
|
||||
step_N: "Step {INDEX}"
|
||||
step_title: "Step title"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Add a picture"
|
||||
change_the_picture: "Change the picture"
|
||||
delete_the_step: "Delete the step"
|
||||
@ -149,7 +151,9 @@ en:
|
||||
employed_materials: "Employed materials"
|
||||
employed_machines: "Employed machines"
|
||||
collaborators: "Collaborators"
|
||||
author: Author
|
||||
creative_commons_licences: "Creative Commons licences"
|
||||
licence: "Licence"
|
||||
themes: "Themes"
|
||||
tags: "Tags"
|
||||
save_as_draft: "Save as draft"
|
||||
|
@ -131,6 +131,7 @@ es:
|
||||
illustration: "Ilustración"
|
||||
add_an_illustration: "Añadir una ilustración"
|
||||
CAD_file: "Fichero CAD"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Extensiones permitidas:"
|
||||
add_a_new_file: "Añadir un nuevo archivo"
|
||||
description: "Description"
|
||||
@ -138,6 +139,7 @@ es:
|
||||
steps: "Pasos"
|
||||
step_N: "Step {INDEX}"
|
||||
step_title: "Título de los pasos"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Añadir imagen"
|
||||
change_the_picture: "Cambiar imagen"
|
||||
delete_the_step: "Eliminar el paso"
|
||||
@ -149,7 +151,9 @@ es:
|
||||
employed_materials: "Material empleados"
|
||||
employed_machines: "Máquinas empleadas"
|
||||
collaborators: "Collaborators"
|
||||
author: Author
|
||||
creative_commons_licences: "Licencias Creative Commons"
|
||||
licence: "Licence"
|
||||
themes: "Themes"
|
||||
tags: "Tags"
|
||||
save_as_draft: "Save as draft"
|
||||
|
@ -131,6 +131,7 @@ fr:
|
||||
illustration: "Illustration"
|
||||
add_an_illustration: "Ajouter un visuel"
|
||||
CAD_file: "Fichier CAO"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Extensions autorisées :"
|
||||
add_a_new_file: "Ajouter un nouveau fichier"
|
||||
description: "Description"
|
||||
@ -138,6 +139,7 @@ fr:
|
||||
steps: "Étapes"
|
||||
step_N: "Étape {INDEX}"
|
||||
step_title: "Titre de l'étape"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Ajouter une image"
|
||||
change_the_picture: "Modifier l'image"
|
||||
delete_the_step: "Supprimer l'étape"
|
||||
@ -149,7 +151,9 @@ fr:
|
||||
employed_materials: "Matériaux utilisés"
|
||||
employed_machines: "Machines utilisées"
|
||||
collaborators: "Les collaborateurs"
|
||||
author: Author
|
||||
creative_commons_licences: "Licences Creative Commons"
|
||||
licence: "Licence"
|
||||
themes: "Thématiques"
|
||||
tags: "Étiquettes"
|
||||
save_as_draft: "Enregistrer comme brouillon"
|
||||
|
@ -131,6 +131,7 @@ it:
|
||||
illustration: "Illustrazione"
|
||||
add_an_illustration: "Aggiungi un'illustrazione"
|
||||
CAD_file: "File CAD"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Estensioni consentite:"
|
||||
add_a_new_file: "Aggiungi nuovo file"
|
||||
description: "Descrizione"
|
||||
@ -138,6 +139,7 @@ it:
|
||||
steps: "Passaggi"
|
||||
step_N: "Passaggio {INDEX}"
|
||||
step_title: "Titolo del passaggio"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Aggiungi un'immagine"
|
||||
change_the_picture: "Cambia immagine"
|
||||
delete_the_step: "Elimina il passaggio"
|
||||
@ -149,7 +151,9 @@ it:
|
||||
employed_materials: "Materiali impiegati"
|
||||
employed_machines: "Macchine impiegate"
|
||||
collaborators: "Collaboratori"
|
||||
author: Author
|
||||
creative_commons_licences: "Licenze Creative Commons"
|
||||
licence: "Licence"
|
||||
themes: "Temi"
|
||||
tags: "Etichette"
|
||||
save_as_draft: "Salva come bozza"
|
||||
|
@ -131,6 +131,7 @@
|
||||
illustration: "Bilde"
|
||||
add_an_illustration: "Legg til en illustrasjon"
|
||||
CAD_file: "CAD-filer"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Tillatte filtyper:"
|
||||
add_a_new_file: "Legg til ny fil"
|
||||
description: "Beskrivelse"
|
||||
@ -138,6 +139,7 @@
|
||||
steps: "Skritt"
|
||||
step_N: "Trinn {INDEX}"
|
||||
step_title: "Tittel på steg"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Legg til bilde"
|
||||
change_the_picture: "Endre bilde"
|
||||
delete_the_step: "Slett trinnet"
|
||||
@ -149,7 +151,9 @@
|
||||
employed_materials: "Materialer brukt"
|
||||
employed_machines: "Maskiner brukt"
|
||||
collaborators: "Samarbeidspartnere"
|
||||
author: Author
|
||||
creative_commons_licences: "Creative Commons lisenser"
|
||||
licence: "Licence"
|
||||
themes: "Temaer"
|
||||
tags: "Etiketter"
|
||||
save_as_draft: "Lagre som utkast"
|
||||
|
@ -131,6 +131,7 @@ pt:
|
||||
illustration: "Foto"
|
||||
add_an_illustration: "Adicionar foto"
|
||||
CAD_file: "Arquivo CAD"
|
||||
CAD_files: "CAD files"
|
||||
allowed_extensions: "Extensões permitidas:"
|
||||
add_a_new_file: "Adicionar novo arquivo"
|
||||
description: "Descrição"
|
||||
@ -138,6 +139,7 @@ pt:
|
||||
steps: "Passos"
|
||||
step_N: "Passo {INDEX}"
|
||||
step_title: "Passo Título"
|
||||
step_image: "Image"
|
||||
add_a_picture: "Adicionar imagem"
|
||||
change_the_picture: "Alterar imagem"
|
||||
delete_the_step: "Deletar este passo"
|
||||
@ -149,7 +151,9 @@ pt:
|
||||
employed_materials: "Materiais utilizados"
|
||||
employed_machines: "Máquinas utilizadas"
|
||||
collaborators: "Colaboradores"
|
||||
author: Author
|
||||
creative_commons_licences: "Licença Creative Commons"
|
||||
licence: "Licence"
|
||||
themes: "Temas"
|
||||
tags: "Tags"
|
||||
save_as_draft: "Salvar como rascunho"
|
||||
|
@ -131,6 +131,7 @@ zu:
|
||||
illustration: "crwdns28728:0crwdne28728:0"
|
||||
add_an_illustration: "crwdns28730:0crwdne28730:0"
|
||||
CAD_file: "crwdns28732:0crwdne28732:0"
|
||||
CAD_files: "crwdns37647:0crwdne37647:0"
|
||||
allowed_extensions: "crwdns28734:0crwdne28734:0"
|
||||
add_a_new_file: "crwdns28736:0crwdne28736:0"
|
||||
description: "crwdns28738:0crwdne28738:0"
|
||||
@ -138,6 +139,7 @@ zu:
|
||||
steps: "crwdns28742:0crwdne28742:0"
|
||||
step_N: "crwdns28744:0{INDEX}crwdne28744:0"
|
||||
step_title: "crwdns28746:0crwdne28746:0"
|
||||
step_image: "crwdns37649:0crwdne37649:0"
|
||||
add_a_picture: "crwdns28748:0crwdne28748:0"
|
||||
change_the_picture: "crwdns28750:0crwdne28750:0"
|
||||
delete_the_step: "crwdns28752:0crwdne28752:0"
|
||||
@ -149,7 +151,9 @@ zu:
|
||||
employed_materials: "crwdns28764:0crwdne28764:0"
|
||||
employed_machines: "crwdns28766:0crwdne28766:0"
|
||||
collaborators: "crwdns28768:0crwdne28768:0"
|
||||
author: crwdns37651:0crwdne37651:0
|
||||
creative_commons_licences: "crwdns28770:0crwdne28770:0"
|
||||
licence: "crwdns37653:0crwdne37653:0"
|
||||
themes: "crwdns28772:0crwdne28772:0"
|
||||
tags: "crwdns28774:0crwdne28774:0"
|
||||
save_as_draft: "crwdns28776:0crwdne28776:0"
|
||||
|
@ -697,6 +697,10 @@ de:
|
||||
trainings_authorization_validity_duration: "Trainings validity period duration"
|
||||
trainings_invalidation_rule: "Trainings automatic invalidation"
|
||||
trainings_invalidation_rule_period: "Grace period before invalidating a training"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "Neu"
|
||||
|
@ -720,6 +720,10 @@ en:
|
||||
trainings_authorization_validity_duration: "Trainings validity period duration"
|
||||
trainings_invalidation_rule: "Trainings automatic invalidation"
|
||||
trainings_invalidation_rule_period: "Grace period before invalidating a training"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
family_account: "Family account"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
|
@ -697,6 +697,10 @@ es:
|
||||
trainings_authorization_validity_duration: "Trainings validity period duration"
|
||||
trainings_invalidation_rule: "Trainings automatic invalidation"
|
||||
trainings_invalidation_rule_period: "Grace period before invalidating a training"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "New"
|
||||
|
@ -720,6 +720,10 @@ fr:
|
||||
trainings_authorization_validity_duration: "Durée de la période de validité des formations"
|
||||
trainings_invalidation_rule: "Invalidation automatique des formations"
|
||||
trainings_invalidation_rule_period: "Période de grâce avant d'invalider une formation"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "Nouveau"
|
||||
|
@ -697,6 +697,10 @@ it:
|
||||
trainings_authorization_validity_duration: "Durata del periodo di validità delle abilitazioni"
|
||||
trainings_invalidation_rule: "Annullamento automatico delle abilitazioni"
|
||||
trainings_invalidation_rule_period: "Periodo di tolleranza prima di invalidare un'abilitazione"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "Nuovo"
|
||||
|
@ -697,6 +697,10 @@
|
||||
trainings_authorization_validity_duration: "Trainings validity period duration"
|
||||
trainings_invalidation_rule: "Trainings automatic invalidation"
|
||||
trainings_invalidation_rule_period: "Grace period before invalidating a training"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "New"
|
||||
|
@ -697,6 +697,10 @@ pt:
|
||||
trainings_authorization_validity_duration: "Trainings validity period duration"
|
||||
trainings_invalidation_rule: "Trainings automatic invalidation"
|
||||
trainings_invalidation_rule_period: "Grace period before invalidating a training"
|
||||
projects_list_member_filter_presence: "Presence of member filter on projects list"
|
||||
projects_list_date_filters_presence: "Presence of dates filter on projects list"
|
||||
project_categories_filter_placeholder: "Placeholder for categories filter in project gallery"
|
||||
project_categories_wording: "Wording used to replace \"Categories\" on public pages"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "Novo"
|
||||
|
@ -697,6 +697,10 @@ zu:
|
||||
trainings_authorization_validity_duration: "crwdns37105:0crwdne37105:0"
|
||||
trainings_invalidation_rule: "crwdns37107:0crwdne37107:0"
|
||||
trainings_invalidation_rule_period: "crwdns37109:0crwdne37109:0"
|
||||
projects_list_member_filter_presence: "crwdns37655:0crwdne37655:0"
|
||||
projects_list_date_filters_presence: "crwdns37657:0crwdne37657:0"
|
||||
project_categories_filter_placeholder: "crwdns37659:0crwdne37659:0"
|
||||
project_categories_wording: "crwdns37661:0crwdne37661:0"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "crwdns37111:0crwdne37111:0"
|
||||
|
@ -38,6 +38,7 @@ Rails.application.routes.draw do
|
||||
get :last_published
|
||||
get :search
|
||||
end
|
||||
get :markdown, on: :member
|
||||
end
|
||||
resources :openlab_projects, only: :index
|
||||
resources :machines
|
||||
@ -46,6 +47,7 @@ Rails.application.routes.draw do
|
||||
resources :themes
|
||||
resources :licences
|
||||
resources :statuses
|
||||
resources :project_categories
|
||||
resources :admins, only: %i[index create destroy]
|
||||
resources :settings, only: %i[show update index], param: :name do
|
||||
patch '/bulk_update', action: 'bulk_update', on: :collection
|
||||
|
9
db/migrate/20230626122844_create_project_categories.rb
Normal file
9
db/migrate/20230626122844_create_project_categories.rb
Normal file
@ -0,0 +1,9 @@
|
||||
class CreateProjectCategories < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :project_categories do |t|
|
||||
t.string :name
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,12 @@
|
||||
class CreateProjectsProjectCategories < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :projects_project_categories do |t|
|
||||
t.belongs_to :project, foreign_key: true, null: false
|
||||
t.belongs_to :project_category, foreign_key: true, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :projects_project_categories, [:project_id, :project_category_id], unique: true, name: :idx_projects_project_categories
|
||||
end
|
||||
end
|
@ -729,5 +729,9 @@ Setting.set('accounting_Error_label', 'Erroneous invoices to refund') unless Set
|
||||
|
||||
Setting.set('external_id', false) unless Setting.find_by(name: 'external_id').try(:value)
|
||||
|
||||
Setting.set('projects_list_member_filter_presence', false) unless Setting.find_by(name: 'projects_list_member_filter_presence')
|
||||
Setting.set('projects_list_date_filters_presence', false) unless Setting.find_by(name: 'projects_list_date_filters_presence')
|
||||
Setting.set('project_categories_filter_placeholder', 'Toutes les catégories') unless Setting.find_by(name: 'project_categories_filter_placeholder').try(:value)
|
||||
Setting.set('project_categories_wording', 'Catégories') unless Setting.find_by(name: 'project_categories_wording').try(:value)
|
||||
Setting.set('family_account', false) unless Setting.find_by(name: 'family_account').try(:value)
|
||||
Setting.set('child_validation_required', false) unless Setting.find_by(name: 'child_validation_required').try(:value)
|
||||
|
141
db/structure.sql
141
db/structure.sql
@ -9,13 +9,6 @@ SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: public; Type: SCHEMA; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
-- *not* creating schema, since initdb creates it
|
||||
|
||||
|
||||
--
|
||||
-- Name: fuzzystrmatch; Type: EXTENSION; Schema: -; Owner: -
|
||||
--
|
||||
@ -2835,6 +2828,37 @@ CREATE SEQUENCE public.profiles_id_seq
|
||||
ALTER SEQUENCE public.profiles_id_seq OWNED BY public.profiles.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_categories; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.project_categories (
|
||||
id bigint NOT NULL,
|
||||
name character varying,
|
||||
created_at timestamp(6) without time zone NOT NULL,
|
||||
updated_at timestamp(6) without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_categories_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.project_categories_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_categories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.project_categories_id_seq OWNED BY public.project_categories.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_steps; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@ -3003,6 +3027,38 @@ CREATE SEQUENCE public.projects_machines_id_seq
|
||||
ALTER SEQUENCE public.projects_machines_id_seq OWNED BY public.projects_machines.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.projects_project_categories (
|
||||
id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
project_category_id bigint NOT NULL,
|
||||
created_at timestamp(6) without time zone NOT NULL,
|
||||
updated_at timestamp(6) without time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.projects_project_categories_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.projects_project_categories_id_seq OWNED BY public.projects_project_categories.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_spaces; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@ -4881,6 +4937,13 @@ ALTER TABLE ONLY public.profile_custom_fields ALTER COLUMN id SET DEFAULT nextva
|
||||
ALTER TABLE ONLY public.profiles ALTER COLUMN id SET DEFAULT nextval('public.profiles_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_categories id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.project_categories ALTER COLUMN id SET DEFAULT nextval('public.project_categories_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_steps id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@ -4916,6 +4979,13 @@ ALTER TABLE ONLY public.projects_components ALTER COLUMN id SET DEFAULT nextval(
|
||||
ALTER TABLE ONLY public.projects_machines ALTER COLUMN id SET DEFAULT nextval('public.projects_machines_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.projects_project_categories ALTER COLUMN id SET DEFAULT nextval('public.projects_project_categories_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_spaces id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@ -5805,6 +5875,14 @@ ALTER TABLE ONLY public.profiles
|
||||
ADD CONSTRAINT profiles_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_categories project_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.project_categories
|
||||
ADD CONSTRAINT project_categories_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: project_steps project_steps_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -5845,6 +5923,14 @@ ALTER TABLE ONLY public.projects
|
||||
ADD CONSTRAINT projects_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories projects_project_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.projects_project_categories
|
||||
ADD CONSTRAINT projects_project_categories_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_spaces projects_spaces_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -6165,6 +6251,13 @@ ALTER TABLE ONLY public.wallets
|
||||
ADD CONSTRAINT wallets_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idx_projects_project_categories; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX idx_projects_project_categories ON public.projects_project_categories USING btree (project_id, project_category_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_abuses_on_signaled_type_and_signaled_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@ -7110,6 +7203,20 @@ CREATE UNIQUE INDEX index_projects_on_slug ON public.projects USING btree (slug)
|
||||
CREATE INDEX index_projects_on_status_id ON public.projects USING btree (status_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_projects_project_categories_on_project_category_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_projects_project_categories_on_project_category_id ON public.projects_project_categories USING btree (project_category_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_projects_project_categories_on_project_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_projects_project_categories_on_project_id ON public.projects_project_categories USING btree (project_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_projects_spaces_on_project_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@ -8290,6 +8397,14 @@ ALTER TABLE ONLY public.projects
|
||||
ADD CONSTRAINT fk_rails_b4a83cd9b3 FOREIGN KEY (status_id) REFERENCES public.statuses(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories fk_rails_ba4a985e85; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.projects_project_categories
|
||||
ADD CONSTRAINT fk_rails_ba4a985e85 FOREIGN KEY (project_id) REFERENCES public.projects(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: statistic_profiles fk_rails_bba64e5eb9; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -8442,6 +8557,14 @@ ALTER TABLE ONLY public.event_price_categories
|
||||
ADD CONSTRAINT fk_rails_dcd2787d07 FOREIGN KEY (event_id) REFERENCES public.events(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: projects_project_categories fk_rails_de9f22810e; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.projects_project_categories
|
||||
ADD CONSTRAINT fk_rails_de9f22810e FOREIGN KEY (project_category_id) REFERENCES public.project_categories(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: cart_item_coupons fk_rails_e1cb402fac; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -8941,7 +9064,9 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20230524083558'),
|
||||
('20230524110215'),
|
||||
('20230525101006'),
|
||||
('20230612123250'),
|
||||
('20230612123250');
|
||||
('20230626103314');
|
||||
('20230626122844'),
|
||||
('20230626122947');
|
||||
|
||||
|
||||
|
@ -25,8 +25,15 @@ class PayZen::Service < Payment::Service
|
||||
order_id: order_id
|
||||
}
|
||||
unless first_item.details['adjustment']&.zero? && first_item.details['other_items']&.zero?
|
||||
params[:initial_amount] = payzen_amount(first_item.amount)
|
||||
params[:initial_amount_number] = 1
|
||||
initial_amount = first_item.amount
|
||||
initial_amount -= payment_schedule.wallet_amount if payment_schedule.wallet_amount
|
||||
if initial_amount.zero?
|
||||
params[:effect_date] = (first_item.due_date + 1.month).iso8601
|
||||
params[:rrule] = rrule(payment_schedule, -1)
|
||||
else
|
||||
params[:initial_amount] = payzen_amount(initial_amount)
|
||||
params[:initial_amount_number] = 1
|
||||
end
|
||||
end
|
||||
pz_subscription = client.create_subscription(**params)
|
||||
|
||||
@ -123,16 +130,21 @@ class PayZen::Service < Payment::Service
|
||||
|
||||
private
|
||||
|
||||
def rrule(payment_schedule)
|
||||
def rrule(payment_schedule, offset = 0)
|
||||
count = payment_schedule.payment_schedule_items.count
|
||||
"RRULE:FREQ=MONTHLY;COUNT=#{count}"
|
||||
"RRULE:FREQ=MONTHLY;COUNT=#{count + offset}"
|
||||
end
|
||||
|
||||
# check if the given transaction matches the given PaymentScheduleItem
|
||||
def transaction_matches?(transaction, payment_schedule_item)
|
||||
transaction_date = Time.zone.parse(transaction['creationDate']).to_date
|
||||
|
||||
transaction['amount'] == payment_schedule_item.amount &&
|
||||
amount = payment_schedule_item.amount
|
||||
if !payment_schedule_item.details['adjustment']&.zero? && payment_schedule_item.payment_schedule.wallet_amount
|
||||
amount -= payment_schedule_item.payment_schedule.wallet_amount
|
||||
end
|
||||
|
||||
transaction['amount'] == amount &&
|
||||
transaction_date >= payment_schedule_item.due_date.to_date &&
|
||||
transaction_date <= payment_schedule_item.due_date.to_date + 7.days
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fab-manager",
|
||||
"version": "6.0.6",
|
||||
"version": "6.0.8",
|
||||
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
||||
"keywords": [
|
||||
"fablab",
|
||||
|
37
test/fixtures/history_values.yml
vendored
37
test/fixtures/history_values.yml
vendored
@ -855,14 +855,45 @@ history_value_100:
|
||||
history_value_101:
|
||||
id: 101
|
||||
setting_id: 100
|
||||
value: 'false'
|
||||
created_at: '2023-03-31 14:38:40.000421'
|
||||
updated_at: '2023-03-31 14:38:40.000421'
|
||||
value: 'Toutes les catégories'
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
invoicing_profile_id: 1
|
||||
|
||||
history_value_102:
|
||||
id: 102
|
||||
setting_id: 101
|
||||
value: 'Catégories'
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
invoicing_profile_id: 1
|
||||
|
||||
history_value_103:
|
||||
id: 103
|
||||
setting_id: 102
|
||||
value: 'false'
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
invoicing_profile_id: 1
|
||||
|
||||
history_value_104:
|
||||
id: 104
|
||||
setting_id: 103
|
||||
value: 'false'
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
|
||||
history_value_105:
|
||||
id: 105
|
||||
setting_id: 104
|
||||
value: 'false'
|
||||
created_at: '2023-03-31 14:38:40.000421'
|
||||
updated_at: '2023-03-31 14:38:40.000421'
|
||||
invoicing_profile_id: 1
|
||||
|
||||
history_value_106:
|
||||
id: 106
|
||||
setting_id: 105
|
||||
value: 'false'
|
||||
created_at: '2023-03-31 14:38:40.000421'
|
||||
updated_at: '2023-03-31 14:38:40.000421'
|
||||
|
12
test/fixtures/project_categories.yml
vendored
Normal file
12
test/fixtures/project_categories.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
project_category_1:
|
||||
id: 1
|
||||
name: Module 1
|
||||
created_at: 2023-06-26 15:39:08.259759000 Z
|
||||
updated_at: 2016-06-26 15:39:08.259759000 Z
|
||||
|
||||
project_category_2:
|
||||
id: 2
|
||||
name: Module 2
|
||||
created_at: 2016-06-26 15:39:08.265840000 Z
|
||||
updated_at: 2016-06-26 15:39:08.265840000 Z
|
2
test/fixtures/project_steps.yml
vendored
2
test/fixtures/project_steps.yml
vendored
@ -7,6 +7,7 @@ project_step_1:
|
||||
created_at: 2016-04-04 15:39:08.259759000 Z
|
||||
updated_at: 2016-04-04 15:39:08.259759000 Z
|
||||
title: Le manche
|
||||
step_nb: 1
|
||||
|
||||
project_step_2:
|
||||
id: 2
|
||||
@ -16,3 +17,4 @@ project_step_2:
|
||||
created_at: 2016-04-04 15:39:08.265840000 Z
|
||||
updated_at: 2016-04-04 15:39:08.265840000 Z
|
||||
title: La presse
|
||||
step_nb: 2
|
1
test/fixtures/projects.yml
vendored
1
test/fixtures/projects.yml
vendored
@ -12,3 +12,4 @@ project_1:
|
||||
state: published
|
||||
slug: presse-puree
|
||||
published_at: 2016-04-04 15:39:08.267614000 Z
|
||||
status_id: 1
|
7
test/fixtures/projects_project_categories.yml
vendored
Normal file
7
test/fixtures/projects_project_categories.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
projects_project_category_1:
|
||||
id: 1
|
||||
project_id: 1
|
||||
project_category_id: 1
|
||||
created_at: 2023-06-26 15:39:08.259759000 Z
|
||||
updated_at: 2016-06-26 15:39:08.259759000 Z
|
28
test/fixtures/settings.yml
vendored
28
test/fixtures/settings.yml
vendored
@ -589,12 +589,36 @@ setting_99:
|
||||
|
||||
setting_100:
|
||||
id: 100
|
||||
name: project_categories_filter_placeholder
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
|
||||
setting_101:
|
||||
id: 101
|
||||
name: project_categories_wording
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
|
||||
setting_102:
|
||||
id: 102
|
||||
name: projects_list_member_filter_presence
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
|
||||
setting_103:
|
||||
id: 103
|
||||
name: projects_list_date_filters_presence
|
||||
created_at: 2023-04-05 09:16:08.000511500 Z
|
||||
updated_at: 2023-04-05 09:16:08.000511500 Z
|
||||
|
||||
setting_104:
|
||||
id: 104
|
||||
name: family_account
|
||||
created_at: 2023-03-31 14:38:40.000421500 Z
|
||||
updated_at: 2023-03-31 14:38:40.000421500 Z
|
||||
|
||||
setting_101:
|
||||
id: 101
|
||||
setting_105:
|
||||
id: 105
|
||||
name: child_validation_required
|
||||
created_at: 2023-03-31 14:38:40.000421500 Z
|
||||
updated_at: 2023-03-31 14:38:40.000421500 Z
|
||||
|
@ -826,6 +826,30 @@ export const settings: Array<Setting> = [
|
||||
last_update: '2022-12-23T14:39:12+0100',
|
||||
localized: 'Url'
|
||||
},
|
||||
{
|
||||
name: 'projects_list_member_filter_presence',
|
||||
value: 'false',
|
||||
last_update: '2022-12-23T14:39:12+0100',
|
||||
localized: 'Projects list member filter presence'
|
||||
},
|
||||
{
|
||||
name: 'projects_list_date_filters_presence',
|
||||
value: 'false',
|
||||
last_update: '2022-12-23T14:39:12+0100',
|
||||
localized: 'Projects list date filters presence'
|
||||
},
|
||||
{
|
||||
name: 'project_categories_filter_placeholder',
|
||||
value: 'Toutes les catégories',
|
||||
last_update: '2022-12-23T14:39:12+0100',
|
||||
localized: 'Placeholder for categories filter in project gallery'
|
||||
},
|
||||
{
|
||||
name: 'project_categories_wording',
|
||||
value: 'Catégories',
|
||||
last_update: '2022-12-23T14:39:12+0100',
|
||||
localized: 'Project categories overridden name'
|
||||
},
|
||||
{
|
||||
name: 'family_account',
|
||||
value: 'false',
|
||||
|
68
test/integration/project_categories_test.rb
Normal file
68
test/integration/project_categories_test.rb
Normal file
@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ProjectCategoriesTest < ActionDispatch::IntegrationTest
|
||||
def setup
|
||||
@admin = User.find_by(username: 'admin')
|
||||
login_as(@admin, scope: :user)
|
||||
end
|
||||
|
||||
test 'create a project_category' do
|
||||
post '/api/project_categories',
|
||||
params: {
|
||||
name: 'Module de fou'
|
||||
}.to_json,
|
||||
headers: default_headers
|
||||
|
||||
# Check response format & project_category
|
||||
assert_equal 201, response.status, response.body
|
||||
assert_match Mime[:json].to_s, response.content_type
|
||||
|
||||
# Check the correct project_category was created
|
||||
res = json_response(response.body)
|
||||
project_category = ProjectCategory.where(id: res[:id]).first
|
||||
assert_not_nil project_category, 'project_category was not created in database'
|
||||
|
||||
assert_equal 'Module de fou', res[:name]
|
||||
end
|
||||
|
||||
test 'update a project_category' do
|
||||
patch '/api/project_categories/1',
|
||||
params: {
|
||||
name: 'Nouveau nom'
|
||||
}.to_json,
|
||||
headers: default_headers
|
||||
|
||||
# Check response format & project_category
|
||||
assert_equal 200, response.status, response.body
|
||||
assert_match Mime[:json].to_s, response.content_type
|
||||
|
||||
# Check the project_category was updated
|
||||
res = json_response(response.body)
|
||||
assert_equal 1, res[:id]
|
||||
assert_equal 'Nouveau nom', res[:name]
|
||||
end
|
||||
|
||||
test 'list all project_categories' do
|
||||
get '/api/project_categories'
|
||||
|
||||
# Check response format & project_category
|
||||
assert_equal 200, response.status, response.body
|
||||
assert_match Mime[:json].to_s, response.content_type
|
||||
|
||||
# Check the list items are ok
|
||||
project_categories = json_response(response.body)
|
||||
assert_equal ProjectCategory.count, project_categories.count
|
||||
end
|
||||
|
||||
test 'delete a project_category' do
|
||||
project_category = ProjectCategory.create!(name: 'Gone too soon')
|
||||
delete "/api/project_categories/#{project_category.id}"
|
||||
assert_response :success
|
||||
assert_empty response.body
|
||||
assert_raise ActiveRecord::RecordNotFound do
|
||||
project_category.reload
|
||||
end
|
||||
end
|
||||
end
|
17
test/integration/projects_test.rb
Normal file
17
test/integration/projects_test.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ProjectsTest < ActionDispatch::IntegrationTest
|
||||
def setup
|
||||
@admin = User.find_by(username: 'admin')
|
||||
login_as(@admin, scope: :user)
|
||||
end
|
||||
|
||||
test 'download markdown file' do
|
||||
get "/api/projects/1/markdown"
|
||||
|
||||
assert_response :success
|
||||
assert_equal "text/markdown", response.content_type
|
||||
end
|
||||
end
|
11
test/models/project_category_test.rb
Normal file
11
test/models/project_category_test.rb
Normal file
@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ProjectCategoryTest < ActiveSupport::TestCase
|
||||
test 'fixtures are valid' do
|
||||
ProjectCategory.find_each do |project_category|
|
||||
assert project_category.valid?
|
||||
end
|
||||
end
|
||||
end
|
15
test/models/project_test.rb
Normal file
15
test/models/project_test.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ProjectTest < ActiveSupport::TestCase
|
||||
test 'fixtures are valid' do
|
||||
Project.find_each do |project|
|
||||
assert project.valid?
|
||||
end
|
||||
end
|
||||
|
||||
test 'relation project_categories' do
|
||||
assert_equal [project_categories(:project_category_1)], projects(:project_1).project_categories
|
||||
end
|
||||
end
|
21
test/services/project_to_markdown_test.rb
Normal file
21
test/services/project_to_markdown_test.rb
Normal file
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ProjectToMarkdownTest < ActiveSupport::TestCase
|
||||
test "ProjectToMarkdown is working" do
|
||||
project = projects(:project_1)
|
||||
service = ProjectToMarkdown.new(project)
|
||||
|
||||
markdown_str = nil
|
||||
|
||||
assert_nothing_raised do
|
||||
markdown_str = service.call
|
||||
end
|
||||
|
||||
assert_includes markdown_str, project.name
|
||||
project.project_steps.each do |project_step|
|
||||
assert_includes markdown_str, project_step.title
|
||||
end
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user