diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..8c8b5f78bf401810fda6d70a282647d1ad7ad6c8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +static/img/bg.jpg filter=lfs diff=lfs merge=lfs -text diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3e0128c84c7ec0791fbb78f816e3826f261ba121 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11 + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +RUN pip install gunicorn + +COPY . . + +CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"] \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..b910cca73f50ab449956e0e01c474127d3b3d75a --- /dev/null +++ b/app.py @@ -0,0 +1,19 @@ +from flask import Flask, render_template, redirect, flash + +from portalrhjobs.ext import configuration, database, login +from portalrhjobs.blueprints import views +import os + + +app = Flask(__name__) + + + +configuration.init_app(app) +database.init_app(app) +login.init_app(app) +views.init_app(app) + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/portalrhjobs/__init__.py b/portalrhjobs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/portalrhjobs/__pycache__/__init__.cpython-311.pyc b/portalrhjobs/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a468e6842779a76986e0e9ac2ce2f77675a43a99 Binary files /dev/null and b/portalrhjobs/__pycache__/__init__.cpython-311.pyc differ diff --git a/portalrhjobs/__pycache__/app.cpython-311.pyc b/portalrhjobs/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afdc7f5ee0601c3fb8b9d372c6ff33e201427d84 Binary files /dev/null and b/portalrhjobs/__pycache__/app.cpython-311.pyc differ diff --git a/portalrhjobs/blueprints/__init__.py b/portalrhjobs/blueprints/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/portalrhjobs/blueprints/__pycache__/__init__.cpython-311.pyc b/portalrhjobs/blueprints/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f4cfa014100760f610dbc38223397b0788e7f30 Binary files /dev/null and b/portalrhjobs/blueprints/__pycache__/__init__.cpython-311.pyc differ diff --git a/portalrhjobs/blueprints/__pycache__/views.cpython-311.pyc b/portalrhjobs/blueprints/__pycache__/views.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddd8531a096937044a072974fd40a32027ace74a Binary files /dev/null and b/portalrhjobs/blueprints/__pycache__/views.cpython-311.pyc differ diff --git a/portalrhjobs/blueprints/rest_api/__init__.py b/portalrhjobs/blueprints/rest_api/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/portalrhjobs/blueprints/rest_api/__pycache__/__init__.cpython-311.pyc b/portalrhjobs/blueprints/rest_api/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4543308cbda58c404b263351d7e44afbdceadc55 Binary files /dev/null and b/portalrhjobs/blueprints/rest_api/__pycache__/__init__.cpython-311.pyc differ diff --git a/portalrhjobs/blueprints/rest_api/__pycache__/ai_function.cpython-311.pyc b/portalrhjobs/blueprints/rest_api/__pycache__/ai_function.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71d86c597cf0b3546e256f090e6f87b22ac3470e Binary files /dev/null and b/portalrhjobs/blueprints/rest_api/__pycache__/ai_function.cpython-311.pyc differ diff --git a/portalrhjobs/blueprints/rest_api/__pycache__/app_telegram.cpython-311.pyc b/portalrhjobs/blueprints/rest_api/__pycache__/app_telegram.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e34459fdf26f601a61bced17e6441a95225050e0 Binary files /dev/null and b/portalrhjobs/blueprints/rest_api/__pycache__/app_telegram.cpython-311.pyc differ diff --git a/portalrhjobs/blueprints/rest_api/ai_function.py b/portalrhjobs/blueprints/rest_api/ai_function.py new file mode 100644 index 0000000000000000000000000000000000000000..c30d4d4aaed599bf56777e8103d9a89daa413f72 --- /dev/null +++ b/portalrhjobs/blueprints/rest_api/ai_function.py @@ -0,0 +1,189 @@ +from PyPDF2 import PdfReader +import json +import requests +import io + + +class AI_RH: + def treat_text(self, pdf): + pdfreader = PdfReader(io.BytesIO(pdf)) + raw_text = '' + for i, page in enumerate(pdfreader.pages): + content = page.extract_text() + if content: + if '•' in content: + content = content.replace("•", "") + raw_text += content + return raw_text + + def create_contents(self, pdf): + text_ = self.treat_text(pdf) + query = f""" +Responda apenas o dicionário em formato json, não acrescente mais nada. +Retorne apenas o dicionário com as informações solicitadas e nada mais. +Texto: + +{text_} +#End +""" + system_cfg = """ +Responda em formato dicionário python padrão. +Responda em português. +Ao encerrar o dicionário, não acrescente mais nada. +Os dados identificadores da pessoa, como nome, localização, email e telefone devem vir com rótulo (Exemplo: Nome: Nome Sobrenome, Email: email_da_pessoa, Telefone: telefone_da_pessoa). +O nome deve ter todos os primeiros caracteres de cada nome em maiúscula. +O telefone pode estar bagunçado no meio do texto, portanto, considere qualquer sequência numeral de 9 números como um telefone válido. +As experiências, ou empresas trabalhadas, é um dos dados mais relevantes. Coloque cada empresa e ano de ingressão e conclusão se houver. +Coloque os as empresas trabalhadas, certificados e cursos em formato de lista, separando cada curso como um item da lista. +O E-MAIL É UMA DAS INFORMAÇÕES MAIS RELEVANTES, procure com atenção. Geralmente ele está escrito no meio do texto, portanto, lembre-se de considerar o que está antes e depois do "@" como o email. Não reorganize as palavras do email de jeito algum, retorne como encontrar. +Geralmente o cargo está anexado à empresa (Exemplo: Cargo em Empresa_X). É IMPORTANTE CLASSIFICAR todas as experiências do mais recente para o mais antigo, considerando sempre a data de início como parâmetro para tal. +O telefone pode estar bagunçado no meio do texto, portanto, considere qualquer sequência numeral de 9 números como um telefone válido. +Considere o dado de habilidades ou skills tudo que você identificar como potencias cursos que a pessoa fez. Faculdades, cursos técnicos e pós graduações devem ficar no campo de Ensino. Por vezes, o candidato não descreve suas habilidades, nesses casos, inclua o campo Habilidades porém retorne vazio. +Algumas vezes a pessoa descreve como "Certificados" suas habilidades. Considere isso também para o campo Habilidades. +Por vezes, a pessoa o descreve como qualificação complementar ou certificações, portanto, considere isso para atribuir à chave de Habilidades. +Sempre leve em consideração a descrição das atividades para o campo Descrição de cada Cargo. +Se atente aos conhecimentos citados no texto e agrupe tudo dentro da chave Habilidades, não renomeie essa chave jamais. +Caso esteja descrito carteira de habilitação no texto, acrescente isso ao campo Habilidades. +Responda em formato dicionário python padrão. +Não comece a resposta com "json", apenas os dados. +Você receberá um texto que será convertido em dicionário python padrão. +Apenas chaves e valores. +Fique atento à formatação. +Quando não encontrar, retorne null no campo correspondente. +Respeite a ordem dos itens abaixo, independente de como esteja escrita no texto. +*** +A idade pode vir descrita ou como data de nascimento. Considere ambos. +Nome: +Idade: +Localização: +Email: (Considere qualquer coisa antes e depois do "@" como email.) +Telefone: (Sempre no formato +55 11 99999-9999) +*** + +*** +Experiências: (o nome do campo sempre no plural, independente da quantidade) +Coloque todos os empregos. Para cada emprego, inclua: +Cargo: +Empresa: +Descrição: (Geralmente a descrição está logo após o cargo e a empresa. Considere sempre que encontrar.) +Data de Início: +Data de Término (se aplicável): +*** + +*** +Educação: (Esse campo sempre se chamará "Educação", independente de como esteja chamando no texto original.) +Considere cursos técnicos, faculdades, cursos superiores, pós graduações. Retorne o campo Educação mesmo se não encontrar nada. Para cada item de formação acadêmica, inclua: +Formação: +Instituição: +Data de conclusão: (se houver) +*** + +*** +Habilidades: +(Esse campo sempre se chamará "Habilidades", independente de como esteja chamando no texto original. Sempre escreva o campo Habilidades mesmo se não encontrar nada.) +Para cada habilidade, inclua: +Conhecimento: + +*** + +Abaixo segue um exemplo de formatação. Toda resposta deve vir nesse exato formato. Por favor, considere apenas a formatação e o nome das chaves, e ignore o conteúdo: + + { + "Nome": "Marcelo Oliveira", + "Idade": 24, + "Localização": "São Paulo, SP", + "Email": "marcelooliveira485_pso@indeedemail.com", + "Telefone": "+55 11 99354 6847", + "Experiências": [ + { + "Cargo": "Auxiliar Técnico em Eletrônica", + "Empresa": "Grupo PLL", + "Descrição: "Manutenção de aparelhos (celulares e tablets), voltado para o reaproveitamento de placas e componentes de dispositivos danificados." + "Data de Início": "Maio de 2019", + "Data de Término": "Outubro de 2023" + } + ], + "Educação": [ + { + "Formação": "Ensino Médio", + "Instituição": "São Paulo, SP", + "Data de Conclusão": "Julho de 2016" + }, + { + "Formação": "Técnico em eletroeletrônica", + "Instituição": "Etec Profª Drª Doroti Quiomi Kanashiro Toyohara", + "Data de Conclusão": "Junho de 2016" + } + ], + "Habilidades": [ + { + "Conhecimento": "Curso: Técnico em eletroeletrônica" + }, + { + "Conhecimento": "Diagnóstico e consertos" + } + ] +} + + +""" + + content = self.chat_completion(system_cfg, query) + print (content) + return content + + + + + + def chat_completion(self, system_content, user_content): + url = "http://localhost:1234/v1/chat/completions" + + payload = json.dumps({ + "model": "lmstudio-community/Meta-Llama-3-8B-Instruct-GGUF", + "messages": [ + { + "role": "system", + "content": system_content + }, + { + "role": "user", + "content": user_content + } + ], + "temperature": 0, + "max_tokens": -1 + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + n_data = json.loads(response.text) + content = n_data["choices"][0]["message"]["content"] + return content + + + + + + + def resumo_ai_function(self, job_dict, resume_dict): + query = f""" + Você é um analista de RH com 10 anos de experiência. + Analise se o candidato possui as qualificações necessárias para a vaga. + Considere o conjunto de habilidades, descrição das experiências anteriores e outras informações que o candidato tenha. + Leia atentamente o currículo. Não invente informações que não esteja explicitamente descrita no currículo. + Vaga: {job_dict} + Currículo: {resume_dict} + Seja simples e breve na sua resposta. Responda no formato do exemplo abaixo. + Exemplo: tem as qualificações que se adequa a vaga. No entanto, não foi mencionado no currículo. Avaliação: %. + + """ + system_cfg = """### Instruction: + Responda em porcentagem de 0 a 100. + Responda em português. + Ao encerrar a resposta, não acrescente mais nada. + """ + content = self.chat_completion(system_cfg, query) + return content \ No newline at end of file diff --git a/portalrhjobs/blueprints/rest_api/resources.py b/portalrhjobs/blueprints/rest_api/resources.py new file mode 100644 index 0000000000000000000000000000000000000000..a8ef04943e66faa47aa6982df7894852a8d28d4b --- /dev/null +++ b/portalrhjobs/blueprints/rest_api/resources.py @@ -0,0 +1,50 @@ +from flask import jsonify, abort, make_response +from flask_restful import Resource, Api +from jsonpickle import encode +from PortalRH.portalrhjobs.ext.models import Vagas +from portalrhjobs.ext.database import db +from datetime import datetime + + + +class VagasApi(Resource): + def get(self): + vagas = Vagas.query.all() or abort(404) + for vaga in vagas: + + vaga.data_abertura = vaga.data_abertura.strftime('%d/%m/%Y') + if vaga.data_fechamento: + vaga.data_fechamento = vaga.data_fechamento.strftime('%d/%m/%Y') + + dados = [vaga.to_dict() for vaga in vagas] + json_data = jsonify(vagas=dados) + resp = make_response(json_data) + resp.headers['Content-Type'] = 'application/json; charset=utf-8' + + return resp + + +class VagasApiItem(Resource): + def get(self, item_id): + + vaga = Vagas.query.filter_by(id=item_id).first() or abort (404) + # Pesquisa o item na lista de itens pelo ID + vaga.data_abertura = vaga.data_abertura.strftime('%d/%m/%Y') + if vaga.data_fechamento: + vaga.data_fechamento = vaga.data_fechamento.strftime('%d/%m/%Y') + dados = vaga.to_dict() + json_data = jsonify(vaga=dados) + resp = make_response(json_data) + resp.headers['Content-Type'] = 'application/json; charset=utf-8' + + return resp + + + +def init_app(app): + + api = Api(app) + api.add_resource(VagasApi, '/api/vagas') + api.add_resource(VagasApiItem, '/api/vagas/') + + diff --git a/portalrhjobs/blueprints/views.py b/portalrhjobs/blueprints/views.py new file mode 100644 index 0000000000000000000000000000000000000000..655cc9f92bf28fcc580060efe2738526849cfa16 --- /dev/null +++ b/portalrhjobs/blueprints/views.py @@ -0,0 +1,456 @@ + +from flask import render_template, abort, request, redirect, url_for, jsonify, Response +from flask_login import login_required, current_user, login_user, logout_user +from flask_caching import Cache +import json +from portalrhjobs.ext.models import User, Vagas, Candidatos +from portalrhjobs.ext.database import db +from portalrhjobs.blueprints.rest_api.ai_function import AI_RH +from sqlalchemy import asc +from datetime import timedelta +import re + +def init_app(app): + cache = Cache(app, config={'CACHE_TYPE': 'simple', 'CACHE_DEFAULT_TIMEOUT': 3600}) + + + ''' + ################################ + ROTA DE LOGIN + ################################ + ''' + + @app.route('/') + def index(): + if current_user.is_authenticated: + return redirect(url_for('home')) + else: + return redirect(url_for('login')) + + @app.route('/login', methods=['GET', 'POST']) + def login(): + if request.method == 'GET': + if current_user.is_authenticated: + return redirect(url_for('home')) + else: + return render_template('login.html') + + error_message = None + if request.method == 'POST': + + email = request.form.get('email') + password = request.form.get('password') + user = User.query.filter_by(email=email).first() + remember_me = bool(int(request.form.get('remember_me'))) + + if user and user.password == password: + login_user(user, remember=remember_me, duration=timedelta(30)) + return redirect(url_for('home')) + else: + error_message = 'Usuário ou senha incorretos' + return render_template('login.html', error_message=error_message) + + + + + + + @app.route('/logout') + @login_required + def logout(): + logout_user() + return redirect(url_for('login')) + + + + + @app.route('/home') + @login_required + def home(): + if current_user.is_authenticated: + + return render_template('index.html') + + + + ''' + ################################ + ROTA DE USUARIOS + ################################ + ''' + + + @app.route('/new_user', methods=['GET', 'POST']) + @login_required + def new_user(): + + if request.method == 'POST': + nome = request.form['nome'] + sobrenome = request.form['sobrenome'] + email = request.form['email'] + username = request.form['username'] + senha = request.form['senha'] + status = request.form['status'] + role = request.form['role'] + + + if User.query.filter_by(nome=nome).first(): + raise RuntimeError(f'{nome} ja esta cadastrado') + + + user = User(nome=nome, sobrenome=sobrenome, email=email, username=username, password=senha, status=status, user_type=role) + + db.session.add(user) + db.session.commit() + db.session.close() + + + + + + + return render_template('new_user.html') + + + + + ''' + ################################ + ROTAS DA PÁGINA DE VAGAS + ################################ + ''' + + @app.route('/vagas') + @login_required + # @cache.cached() + def vagas(): + vagas = Vagas.query.filter_by(status='ativo').order_by(Vagas.data_abertura.desc()).all() + return render_template('vagas.html', vagas=vagas) + + + @app.route('/vagas/', methods=['GET', 'POST']) + @login_required + #@cache.cached() + def vaga(vaga_id): + vaga = Vagas.query.filter_by(id=vaga_id).first() + candidatos = Candidatos.query.filter_by(vaga_id=vaga_id).all() + + return render_template('vaga.html', vaga=vaga, candidatos=candidatos) + + + def count_status(candidatos, status): + count = 0 + for candidato in candidatos: + if candidato.status == status: + count += 1 + return count + app.add_template_global(count_status, 'count_status') + + + + @app.route('/nova_vaga', methods=['GET', 'POST']) + @login_required + def nova_vaga(): + + if request.method == 'POST': + cargo = request.form['cargo'] + dept = request.form['departamento'] + local = request.form['localizacao'] + nivel = request.form['nivel'] + escopo = request.form['escopo'] + requisitos = request.form['requisitos'] + qualificacoes = request.form['qualificacoes'] + info_adicionais = request.form['info-adicionais'] + if not info_adicionais.strip(): + info_adicionais = None + + + vaga = Vagas(cargo=cargo, departamento=dept, local=local, nivel=nivel, escopo_vaga=escopo, requisitos=requisitos, qualificacoes=qualificacoes, info_adicionais=info_adicionais) + db.session.add(vaga) + db.session.commit() + db.session.refresh(vaga) + + vaga_id = vaga.id + cache.clear() + return redirect(url_for('vaga', vaga_id=vaga_id)) + + + + + + return render_template('nova_vaga.html') + + + @app.route('/editar_vaga/', methods=['GET', 'POST']) + @login_required + + def editar_vaga(vaga_id): + # Recupere a vaga do banco de dados + vaga = Vagas.query.get(vaga_id) + + if request.method == 'POST': + + + vaga.cargo = request.form['cargo'] + vaga.dept = request.form['departamento'] + vaga.local = request.form['localizacao'] + vaga.nivel = request.form['nivel'] + vaga.escopo = request.form['escopo'] + vaga.requisitos = request.form['requisitos'] + vaga.qualificacoes = request.form['qualificacoes'] + vaga.info_adicionais = request.form['info-adicionais'] + if not vaga.info_adicionais.strip(): + vaga.info_adicionais = None + + db.session.commit() + cache.clear() + return redirect(url_for('vaga', vaga_id=vaga_id)) + + return render_template('editar_vaga.html', vaga=vaga) + + + @app.route('/encerrar_vaga/') + @login_required + def encerrar_vaga(vaga_id): + vaga = Vagas.query.filter_by(id=vaga_id).first() + vaga.status = 'inativo' + db.session.commit() + cache.clear() + return redirect(url_for('vaga', vaga_id=vaga_id)) + + + @app.route('/reabrir_vaga/') + @login_required + def reabrir_vaga(vaga_id): + vaga = Vagas.query.filter_by(id=vaga_id).first() + vaga.status = 'ativo' + db.session.commit() + cache.clear() + return redirect(url_for('vaga', vaga_id=vaga_id)) + + + + + @app.route('/candidatos/vaga=', methods=['GET', 'POST']) + @login_required + #@cache.cached() + def candidatos_vaga_id(vaga_id): + + vaga = Vagas.query.filter_by(id=vaga_id).first() + candidatos = Candidatos.query.filter_by(vaga_id=vaga_id).all() + message = None + pdf_ = None + if request.method == 'POST': + message, pdf_ = processar_pdf(vaga_id) + if pdf_: + message = f'Arquivo PDF enviado com sucesso!\nIniciando o processo de extração de texto...' + salvar_candidato(pdf_, pdf_.id) + cache.clear() + else: + message = 'Nenhum arquivo enviado' + return 'Tudo certo', 204, {'X-Message': 'Currículo cadastrado com sucesso!'} + + return render_template('candidatos_vaga_id.html', vaga=vaga, message=message, candidatos=candidatos) + + def processar_pdf(vaga_id): + if 'file' not in request.files: + return 'Nenhum arquivo enviado' + pdf_file = request.files['file'] + if pdf_file.filename == '': + return 'Nome do arquivo vazio' + + pdf_data = pdf_file.read() + novo_pdf = salvar_pdf(vaga_id, pdf_data) + return 'Arquivo PDF enviado com sucesso', novo_pdf + + + def salvar_pdf(vaga_id, pdf_data): + novo_pdf = Candidatos(vaga_id=vaga_id, curriculo_pdf=pdf_data) + db.session.add(novo_pdf) + db.session.commit() + db.session.refresh(novo_pdf) + return novo_pdf + + + @app.route('/candidatos/vaga=/mining-text', methods=['POST']) + @login_required + def extrair_texto(pdf): + pdf_content = pdf.curriculo_pdf + texto_extraido = AI_RH().create_contents(pdf_content) + return texto_extraido + + def salvar_candidato(pdf, pdf_id): + content = extrair_texto(pdf) + json_string = '\n'.join(content.split('\n')) + data = json.loads(content) + candidato = Candidatos.query.get(pdf_id) + candidato.nome = data["Nome"] + candidato.idade = data["Idade"] + candidato.localizacao = data["Localização"] + candidato.email = data["Email"] + candidato.telefone = data["Telefone"] + candidato.experiencia = data["Experiências"] + candidato.educacao = data["Educação"] + candidato.habilidades = data["Habilidades"] + db.session.commit() + + + + @app.route('/notification') + @login_required + def notification(): + + return render_template('notification.html') + + + ################################################ + ### ROTA DE CANDIDATOS ### + ################################################ + + @app.route('/candidatos') + @login_required + # @cache.cached() + def candidatos(): + vagas = Vagas.query.filter_by(status='ativo').order_by(Vagas.cargo.asc()).all() + candidatos = Candidatos.query.order_by(asc(Candidatos.nome)).all() + return render_template('candidatos.html', candidatos=candidatos, vagas=vagas, vaga=vaga) + + @app.route('/candidatos/') + @login_required + def candidatos_(): + return redirect(url_for('candidatos')) + + @app.route('/candidatos/', methods=['GET', 'POST']) + @login_required + def candidato(candidato_id): + candidato = Candidatos.query.filter_by(id=candidato_id).first() + candidato_dict = vars(candidato) + + candidato_dict.pop('curriculo_pdf', None) + candidato_dict.pop('data_cadastro', None) + print (candidato_dict) + + if request.method == 'POST': + if 'acao_fase' in request.form: + acao = request.form['acao_fase'].lower() + if acao == 'avancar': + novo_status = avancar_fase(candidato) + + elif acao == 'retroceder': + novo_status = retroceder_fase(candidato) + if novo_status: + cache.clear() + novo_status = candidato.status + return jsonify({'novoStatus': novo_status}), 200 + + return render_template('candidatos_candidato_id.html', candidato=candidato) + + def get_next_status(): + return { + 'Currículo enviado': 'Background check', + 'Background check': 'Entrevista técnica', + 'Entrevista técnica': 'Aprovado', + 'Aprovado': None + } + def get_previous_status(): + return { + 'Background check': 'Currículo enviado', + 'Entrevista técnica': 'Background check', + 'Aprovado': 'Entrevista técnica' + } + + def retroceder_fase(candidato): + status_atual = candidato.status + status_anterior = get_previous_status().get(status_atual) + if status_anterior: + candidato.status = status_anterior + db.session.commit() + db.session.refresh(candidato) + return candidato + return candidato + + def avancar_fase(candidato): + status_atual = candidato.status + proximo_status = get_next_status().get(status_atual) + if proximo_status: + candidato.status = proximo_status + db.session.commit() + db.session.refresh(candidato) + return candidato + return candidato + + + @app.route('/candidatos//atualizar', methods=['POST']) + @login_required + def atualizar_candidato(candidato_id): + candidato = Candidatos.query.get(candidato_id) + data = request.get_json() + nome = data.get('nome', '') + telefone = data.get('telefone', '') + email = data.get('email', '') + localizacao = data.get('localizacao', '') + experiencia = data.get('experiencia', []) + educacao = data.get('educacao', []) + habilidades = data.get('habilidades', []) + candidato.nome = nome + candidato.telefone = telefone + candidato.email = email + candidato.localizacao = localizacao + candidato.experiencia = experiencia + candidato.educacao = educacao + candidato.habilidades = habilidades + db.session.commit() + db.session.refresh(candidato) + cache.clear() + return jsonify({'message': 'Dados atualizados com sucesso!'}) + + + + @app.route('/candidatos//resumo', methods=['POST']) + @login_required + def resumo_candidato(candidato_id): + def to_dict(objeto, atributos): + candidato_dict = {attr: getattr(objeto, attr) for attr in atributos if not attr.startswith('_')} + remover_colunas(candidato_dict) + return candidato_dict + + def remover_colunas(dicionario): + colunas_para_remover = ['curriculo_pdf', 'data_cadastro'] # Lista de colunas para remover + for coluna in colunas_para_remover: + dicionario.pop(coluna, None) + + def obter_atributos_vaga(candidato): + vaga_id = candidato.vaga_id # Obtém a vaga associada ao candidato + if vaga_id: + vaga_associada = Vagas.query.get(vaga_id) + if vaga_associada: + return { + 'escopo_vaga': vaga_associada.escopo_vaga, + 'requisitos': vaga_associada.requisitos, + 'qualificacoes': vaga_associada.qualificacoes, + 'info_adicionais': vaga_associada.info_adicionais + } + else: + return None # Retorna None se não houver vaga associada ao candidato + candidato = Candidatos.query.get(candidato_id) + atributos_para_incluir = [key for key in vars(candidato) if not key.startswith('_')] # Obtendo todos os atributos, exceto os que começam com "_" + candidato_dict = to_dict(candidato, atributos_para_incluir) + vaga_dict = obter_atributos_vaga(candidato) + + if request.method == 'POST': + print ('starting') + resumo_ia = AI_RH().resumo_ai_function(vaga_dict, candidato_dict) + + partes = resumo_ia.split('Avaliação:') + resumo_candidato = partes[0].strip() + avaliacao = partes[1].strip().replace('.', '') if len(partes) > 1 else None + print (resumo_candidato) + print (avaliacao) + + candidato.resumo_ia = resumo_candidato + candidato.avaliacao_ia = avaliacao + db.session.commit() + return jsonify({'resumo_candidato': resumo_candidato, 'avaliacao': avaliacao}) + + + diff --git a/portalrhjobs/ext/__init__.py b/portalrhjobs/ext/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/portalrhjobs/ext/__pycache__/__init__.cpython-311.pyc b/portalrhjobs/ext/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da831b33eb2bcc4b5467dfc2e26f5b506e9b56d7 Binary files /dev/null and b/portalrhjobs/ext/__pycache__/__init__.cpython-311.pyc differ diff --git a/portalrhjobs/ext/__pycache__/admin.cpython-311.pyc b/portalrhjobs/ext/__pycache__/admin.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6a3a95bbda413d419d4fdcdb0891f191fd875a3 Binary files /dev/null and b/portalrhjobs/ext/__pycache__/admin.cpython-311.pyc differ diff --git a/portalrhjobs/ext/__pycache__/auth.cpython-311.pyc b/portalrhjobs/ext/__pycache__/auth.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f0b59ee737854a5619ab7d59546064d49ab5203 Binary files /dev/null and b/portalrhjobs/ext/__pycache__/auth.cpython-311.pyc differ diff --git a/portalrhjobs/ext/__pycache__/configuration.cpython-311.pyc b/portalrhjobs/ext/__pycache__/configuration.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8818e099324bfa8007a83c7a91d35967b44f57b7 Binary files /dev/null and b/portalrhjobs/ext/__pycache__/configuration.cpython-311.pyc differ diff --git a/portalrhjobs/ext/__pycache__/database.cpython-311.pyc b/portalrhjobs/ext/__pycache__/database.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..518d31146c2a9920f6ed9a25ca81acd0abdd7d91 Binary files /dev/null and b/portalrhjobs/ext/__pycache__/database.cpython-311.pyc differ diff --git a/portalrhjobs/ext/__pycache__/login.cpython-311.pyc b/portalrhjobs/ext/__pycache__/login.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d725ade21c5700b1a10dd26a1f6e93df60f1a858 Binary files /dev/null and b/portalrhjobs/ext/__pycache__/login.cpython-311.pyc differ diff --git a/portalrhjobs/ext/__pycache__/models.cpython-311.pyc b/portalrhjobs/ext/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d9eca59d7f367b5d7f2ed0527fea31898809bab Binary files /dev/null and b/portalrhjobs/ext/__pycache__/models.cpython-311.pyc differ diff --git a/portalrhjobs/ext/admin.py b/portalrhjobs/ext/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..4cfa7c5ba639356bcf3686d604d4b09385e5cb99 --- /dev/null +++ b/portalrhjobs/ext/admin.py @@ -0,0 +1,28 @@ +from flask_admin import Admin +from flask_admin.base import AdminIndexView +from flask_admin.contrib import sqla +from flask_simplelogin import login_required +from werkzeug.security import generate_password_hash + +from portalrhjobs.ext.database import db +from PortalRH.portalrhjobs.ext.models import User + +# Proteger o admin com login via Monkey Patch +AdminIndexView._handle_view = login_required(AdminIndexView._handle_view) +sqla.ModelView._handle_view = login_required(sqla.ModelView._handle_view) +admin = Admin() + + +class UserAdmin(sqla.ModelView): + column_list = ['username'] + can_edit = False + + def on_model_change(self, form, model, is_created): + model.password = generate_password_hash(model.password) + + +def init_app(app): + admin.name = app + admin.template_mode = "bootstrap3" + admin.init_app(app) + admin.add_view(UserAdmin(User, db.session)) \ No newline at end of file diff --git a/portalrhjobs/ext/auth.py b/portalrhjobs/ext/auth.py new file mode 100644 index 0000000000000000000000000000000000000000..cf12a4fc63a3c968d8a024e2c057eb36c80ada51 --- /dev/null +++ b/portalrhjobs/ext/auth.py @@ -0,0 +1,31 @@ +from flask_login import LoginManager, UserMixin +from werkzeug.security import check_password_hash, generate_password_hash +from portalrhjobs.ext.database import db +from PortalRH.portalrhjobs.ext.models import User + +login_manager = LoginManager() + +def init_app(app): + def verify_login(user): + """Valida o usuario e senha para efetuar o login""" + username = user.get('username') + password = user.get('password') + if not username or not password: + return False + existing_user = User.query.filter_by(username=username).first() + if not existing_user: + return False + if check_password_hash(existing_user.password, password): + return True + return False + + + def create_user(username, password): + """Registra um novo usuario caso nao esteja cadastrado""" + if User.query.filter_by(username=username).first(): + raise RuntimeError(f'{username} ja esta cadastrado') + user = User(username=username, password=generate_password_hash(password)) + db.session.add(user) + db.session.commit() + return user + diff --git a/portalrhjobs/ext/configuration.py b/portalrhjobs/ext/configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..68560e502ecfc21945b4defa177a4018a31616e5 --- /dev/null +++ b/portalrhjobs/ext/configuration.py @@ -0,0 +1,6 @@ +from dynaconf import FlaskDynaconf + + + +def init_app(app): + FlaskDynaconf(app, settings_files=['settings.toml']) diff --git a/portalrhjobs/ext/database.py b/portalrhjobs/ext/database.py new file mode 100644 index 0000000000000000000000000000000000000000..ea8791747b8b2a6bd5c9e694374b27a0b4c023d5 --- /dev/null +++ b/portalrhjobs/ext/database.py @@ -0,0 +1,12 @@ +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + + +db = SQLAlchemy() + + +def init_app(app): + migrate = Migrate(app, db) + db.init_app(app) + + diff --git a/portalrhjobs/ext/login.py b/portalrhjobs/ext/login.py new file mode 100644 index 0000000000000000000000000000000000000000..187ab4804943ab36900dfa77796751281464484f --- /dev/null +++ b/portalrhjobs/ext/login.py @@ -0,0 +1,17 @@ +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required +from ..ext.models import User + + + +def init_app(app): + login_manager = LoginManager(app) + login_manager.init_app(app) + login_manager.login_view = 'login' + + + @login_manager.user_loader + def load_user(user_id): + return User.query.get(user_id) + + + diff --git a/portalrhjobs/ext/models.py b/portalrhjobs/ext/models.py new file mode 100644 index 0000000000000000000000000000000000000000..abb9acf8a552a943f581f3e7332b88718d08bd48 --- /dev/null +++ b/portalrhjobs/ext/models.py @@ -0,0 +1,57 @@ + +from datetime import datetime +from sqlalchemy import DateTime, Sequence, LargeBinary, JSON, func +from sqlalchemy_serializer import SerializerMixin +from ..ext.database import db +from flask_login import UserMixin + +class User(db.Model, UserMixin): + __tablename__ = 'users' + id = db.Column(db.Integer, primary_key=True, nullable=False ) + nome = db.Column(db.String(128), nullable=False) + sobrenome = db.Column(db.String(128), nullable=False) + email = db.Column(db.String(128), nullable=False) + username = db.Column(db.String(128), nullable=False) + password = db.Column(db.String(128), nullable=False) + dt_criacao = db.Column(DateTime, default=datetime.now(), nullable=False) + status = db.Column(db.String(128), nullable=False) + user_type = db.Column(db.String(128), nullable=False) + + + +class Vagas(db.Model, SerializerMixin): + __tablename__ = 'vagas' + id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False ) + cargo = db.Column(db.String(128), nullable=False) + departamento = db.Column(db.String(128), nullable=False) + local = db.Column(db.String(128), nullable=False) + nivel = db.Column(db.String(128), nullable=False) + escopo_vaga = db.Column(db.Text, nullable=False) + requisitos = db.Column(db.Text, nullable=False) + qualificacoes = db.Column(db.Text, nullable=False) + info_adicionais = db.Column(db.Text, nullable=True) + data_abertura = db.Column(db.Date, default=datetime.now(), nullable=False) + data_fechamento = db.Column(db.Date, nullable=True) + status = db.Column(db.String(128), default='ativo', nullable=False) + + +class Candidatos(db.Model, SerializerMixin): + __tablename__ = 'candidatos' + id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=False ) + vaga_id = db.Column(db.Integer, db.ForeignKey('vagas.id'), nullable=False) + vaga = db.relationship('Vagas', backref='candidatos') + nome = db.Column(db.String(128), nullable=True) + idade = db.Column(db.String(128), nullable=True) + email = db.Column(db.String(128), nullable=True) + telefone = db.Column(db.String(128), nullable=True) + localizacao = db.Column(db.String(128), nullable=True) + experiencia = db.Column(JSON, nullable=True) + educacao = db.Column(JSON, nullable=True) + habilidades = db.Column(JSON, nullable=True) + curriculo_pdf = db.Column(LargeBinary(length=(2**32)-1), nullable=False) + status = db.Column(db.String(128), default='Currículo enviado', nullable=False) + data_cadastro = db.Column(DateTime, nullable=False, server_default=func.now()) + data_encerramento = db.Column(DateTime, nullable=True) + motivo_encerramento = db.Column(db.Text, nullable=True) + resumo_ia = db.Column(db.Text, nullable=True) + avaliacao_ia = db.Column(db.Text, nullable=True) \ No newline at end of file diff --git a/portalrhjobs/vercel.json b/portalrhjobs/vercel.json new file mode 100644 index 0000000000000000000000000000000000000000..c2152dc0e5c082aea8998ee8dd84809cd2f0ada0 --- /dev/null +++ b/portalrhjobs/vercel.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "builds": [ + { "src": "app.py", "use": "@vercel/python" } + ] +, "routes": [ + { "src": "/(.*)", "dest": "app.py" } +] +} + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..9c76a0aaf35973952336d5be4e240adb3fc437bd Binary files /dev/null and b/requirements.txt differ diff --git a/settings.toml b/settings.toml new file mode 100644 index 0000000000000000000000000000000000000000..4052b796d9c9b16618bc5399b3f26c341c571d0b --- /dev/null +++ b/settings.toml @@ -0,0 +1,5 @@ +[default] +SQLALCHEMY_DATABASE_URI = "mysql://smoreira:Inetum2024_Samuel@10.123.57.214:3306/dev_rh" +SECRET_KEY = "my_secret_key" +JSON_AS_ASCII = true +FLASK_RUN_HOST="0.0.0.0" diff --git a/static/base.css b/static/base.css new file mode 100644 index 0000000000000000000000000000000000000000..cd599b7d8c0561de59490e0396207128067b1b5f --- /dev/null +++ b/static/base.css @@ -0,0 +1,245 @@ + * { + margin: 0; + padding: 0; + font-family: 'Poppins'; + + + } + + :root { + --primary-blue: #232d4b; + --secondary-blue: #005573; + --blue-one: rgb(49, 133, 156); + --purple-blue: rgb(90, 112, 197); + --mineral-green: #00aa9b; + --light-grey: #fcfcfc; + --grey: #f2f2f2; + --primary-red: #f04641; + --secondary-red: #f5908d; + --primary-orange: #f89235; + + } + + ::-webkit-scrollbar { + width: 4px; + } + + ::-webkit-scrollbar-track { + background-color: var(--primary-blue); + padding: 2px; + } + + ::-webkit-scrollbar-thumb { + background-color: var(--secondary-blue); + border-radius: 5px; + + } + +#header-field { + background: var(--secondary-blue); + position: absolute; + top: 0; + left: 0; + text-align: center; + border-bottom-right-radius: 10px; + color: white; + font-weight: 700; + width: 300px; + padding: 15px; + font-size: 30px; + user-select: none; + cursor: pointer; +} + + + nav { + display: flex; + justify-content: space-around; + align-items: center; + font-family: Poppins; + height: auto; + padding: 33px 0 26px; + max-height: 110px; + + } + +a { + color: inherit; + text-decoration: none; +} + + html, + body { + font-family: 'Poppins'; + position: relative; + + + } + + .gradient-overlay { + position: fixed; + top: 60%; + left: 0; + width: 100%; + height: 100%; + background: #ffffff; + z-index: -3; + overflow: hidden; + transform: translateY(-50%); + } + + .gradient-overlay::after { + content: ""; + position: absolute; + top: 60%; + left: 0; + width: 100%; + height: 90vh; + background: var(--primary-blue); + z-index: -3; + } + + .nav-list { + list-style: none; + display: flex; + color: white; + } + + + + + .nav-list li { + letter-spacing: 0px; + margin-left: 32px; + align-items: center; + margin-top: 10px; + padding: 0 11.5px; + + } + +.active{ + color: var(--mineral-green); + } + + a.navbar-a-block-branding { + color: inherit; + text-decoration: none; + font-size: 1.125rem; + font-weight: 500; + cursor: pointer; +} + + #vagas-navbar-a.active { + color: var(--mineral-green); + } + + #username { + color: #fff; + font-size: 1.125rem; + font-weight: 300; + margin-left: 400px; + } + + + + + .mobile-menu { + display: none; + cursor: pointer; + } + + + .mobile-menu div { + width: 32px; + height: 2px; + background: #fff; + margin: 8px; + transition: 0.3s; + + } + + header { + background-color: var(--primary-blue); + display: flex; + justify-content: flex-start; + padding: 20px 0 26px; + height: 88px; + } + + + .branding { + flex: 0 1 auto; + + } + + .block-branding>a { + margin-left: 120px; + margin-right: 15px; + } + + #logout-link { + margin-left: 30px; + } + + + @media (max-width:999px) { + body { + overflow-x: hidden; + + } + + .nav-list { + position: absolute; + top: 8vh; + right: 0; + width: 50vw; + height: 92vh; + background: var(--primary-blue); + flex-direction: column; + align-items: center; + justify-content: space-around; + transform: translateX(100%); + transition: transform 0.3s ease-in; + padding: 1px; + } + + .nav-list li { + margin-left: 0; + opacity: 0; + } + + .mobile-menu { + display: block; + } + + .card-body { + margin: auto; + max-width: 200px; + min-width: 200px; + text-decoration: none; + + } + + } + + + + + .nav-list.active { + transform: translateX(0); + } + + @keyframes navLinkFade { + from { + opacity: 0; + transform: translateX(50px); + } + + to { + opacity: 1; + transform: translateX(0); + } + } + + + diff --git a/static/candidatos.css b/static/candidatos.css new file mode 100644 index 0000000000000000000000000000000000000000..fc448b7b22c659b3fac16beb7d395934dc31e090 --- /dev/null +++ b/static/candidatos.css @@ -0,0 +1,276 @@ +body{ + height: 150vh; +} + + +.card-container { + flex-wrap: wrap; + margin: 10px auto; + display: grid; + grid-template-columns: repeat(3, 1fr); + padding: 40px; +} + +.container-candidatos { + + margin: 50px auto; + padding: 50px; + position: relative; + background-color: var(--grey); + box-sizing: border-box; + border-radius: 10px; + width: 80%; + box-shadow: + -3px 0 5px rgba(0, 0, 0, 0.3), + 3px 0 5px rgba(0, 0, 0, 0.3); + place-content: center; + +} +.card-content{ + + background: var(--light-grey); + margin: 10px; + border-radius: 10px; + min-width: 420px; + padding-bottom: 10px; + +} + +.card-content:hover { + transition: all 0.3s; + transform: translateX(10px); + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); +} + +.header-page-candidatos { + padding-left: 10px; +} + +.card-column { + padding-top: 86px +} + + +.filter-head { + list-style: none; + display: flex; + position: absolute; + width: 60%; + left: 30%; + top: 0; + margin-top: 10px; + background: linear-gradient(to left, var(--secondary-blue), var(--blue-one)); + ; + padding: 10px; + color: white; + border-radius: 40px; + align-items: center; + user-select: none; +} + +.filter-head li { + display: flex; + position: relative; + align-items: center; + gap: 10px; + justify-content: center; + margin: 0 auto; +} + + +.dropdown-funnel { + display: inline-block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + +} + + +.dropdown-menu { + display: none; + position: absolute; + top: 100%; + left: 0; + background-color: #f9f9f9; + min-width: 100px; + padding: 5px; + border: 1px solid #ccc; + white-space: nowrap; +} + + + + +.dropdown-menu li { + display: flex; + padding: 5px 10px 6px 10px; + cursor: pointer; + justify-content: space-around; + + +} + +.dropdown { + width: 200px; +} + +i { + font-size: 30px; +} + + + +.filter-content { + display: flex; + align-items: center; + gap: 20px; +} + +#status-dropdown { + width: auto; + text-align: center; + min-width: 160px; + height: 26px; + border: none; + opacity: 0.9; +} + +#status-dropdown:focus-visible { + outline: 1px solid var(--grey); +} + +.container-input { + position: relative; +} + +.input-search { + width: 150px; + padding: 10px 0px 10px 40px; + border-radius: 9999px; + border: solid 1px #333; + transition: all .2s ease-in-out; + outline: none; + background-color: linear-gradient(to bottom, #f9f9f9, #ccc); + opacity: 0.8; +} + +.container-input svg { + position: absolute; + top: 50%; + left: 10px; + transform: translate(0, -50%); +} + +.input-search:focus { + opacity: 0.8; + width: 300px; +} + +.icons-filter { + display: flex; + gap: 60px +} + + +#not-found { + color: black; +} + +.no-cards-available{ + + display: flex; + flex-direction: column; + width: 100%; + height: 150px; + align-content: flex-start; + align-items: center; + justify-content: center; + color: rgba(0, 0, 0, 0.16); +} + +.no-cards-available i{ + font-size: 90px; +} + + +.status-bar { + height: 5px; + width: 100%; + border-top-right-radius: 10px; +} + +.status-bar{ + + background: linear-gradient(to right, var(--blue-one) 65%, var(--secondary-blue) 90%); + +} + +.status-bar.Background_check{ + background: linear-gradient(to right, var(--blue-one) 65%, var(--primary-orange) 90%); +} + +.status-bar.Entrevista_técnica{ + background: linear-gradient(to right, var(--blue-one) 65%, var(--purple-blue) 90%); +} + +.status-bar.Aprovado{ + background: linear-gradient(to right, var(--blue-one) 65%, var(--mineral-green) 90%);} + +.div-card-title, .div-card-body{ + padding: 5px 10px; +} + +.div-card-title p{ + margin: 1px; +} + +#card-nome{ + font-size: 20px; + font-weight: 500; +} + +.page-content { + position: absolute; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 9999; + overflow: hidden; + display: none; +} + +div.modal-choose-vaga{ + display: flex; + flex-direction: column; + align-items: center; + width: 600px; + height: 300px; + position: absolute; + left: 50%; + top: 20%; + transform: translate(-50%, -50%); + padding: 20px; + border-radius: 10px; + background: var(--grey); + +} + +i#new-candidate{ + font-size: 60px; + margin-top: 50px; + color: rgba(0, 0, 0, 0.5); + cursor: pointer; +} + +select#select-vaga{ + text-align: center; + padding: 2px 10px; +} + +div.card-avaliacao{ + position: absolute; + left: 90%; + margin-top: 5px; +} \ No newline at end of file diff --git a/static/candidatos_candidato_id.css b/static/candidatos_candidato_id.css new file mode 100644 index 0000000000000000000000000000000000000000..3059973edc2b6590b7aacc05851106d90e209c41 --- /dev/null +++ b/static/candidatos_candidato_id.css @@ -0,0 +1,351 @@ +div.container-candidato-id-page{ + display: grid; + position: relative; + margin: 50px auto; + background-color: var(--grey); + box-sizing: border-box; + border-radius: 10px; + width: 97%; + box-shadow: 0px 6px 5px rgba(0, 0, 0, 0.3); + min-height: 800px; +} + +div.card-candidato-id-page{ + position: relative; + min-height: 130px; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; +} + +div.candidato-info{ + display: flex; + height: 43vh; + background: var(--light-grey); + border-radius: 20px; + width: 350px; + margin: 120px auto 0 auto; + text-align: center; + justify-content: center; + position: relative; + padding: 20px; + box-shadow: 0px -6px 5px rgba(0, 0, 0, 0.3); + flex-direction: column; + +} + + + +i#candidato-icon{ + font-size: 100px; + color: rgba(0, 0, 0, 0.3); + margin-top: 30px; +} + +i#candidato-robot{ + font-size: 30px; +} + +span#candidato-nome{ + font-size: 25px; + font-weight: 500; +} + +span#candidato-email{ + font-size: 14px; +} + +span#candidato-telefone{ + font-size: 16px; +} + +div.candidato-content{ + border: 1px solid grey; + padding: 15px; + border-radius: 5px; +} + +p.candidato-content-title{ + font-size: 15px; + font-weight: 800; + margin: 1px; + color: rgba(0, 0, 0, 0.8); + +} + +span.candidato-content-info{ + font-size: 17px; +} + + +div.candidato-div-resumo{ + display: grid; + grid-template-columns: 400px 1fr; +} +div.container-candidato-tabs { + margin: 10px; + width: 100%; + grid-column: 2; + transition: all 0.5s ease; +} +div.tab_trigger{ + display: flex; + align-items: center; +} +div.tab_trigger ul { + padding: 0; + margin: 0; + list-style: none; +} + +div.tab_trigger ul li { + display: inline-block; + margin-right: 2px; + border-radius: 12px 12px 0 0; + background-color: var(--secondary-blue); + color: white; + padding: 8px 25px; + cursor: pointer; + transition: all 0.3s; + font-weight: 600; + user-select: none; +} + +div.tab_trigger ul li:hover, +.tab_trigger ul li.active { + background-color: var(--blue-one); +} + +div.tab_content_box { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 40px; + min-height: 300px; + margin-right: 40px; + margin-bottom: 20px; + font-size: 20px; + color: #000; + background-color: var(--light-grey); + padding: 25px; + border-radius: 0 12px 12px 12px; + box-shadow: 0px -6px 5px rgba(0, 0, 0, 0.3); +} + +div.tab_content_box:nth-child(4) { + display: flex; + /* Adicione estilos específicos aqui */ +} +div.info-buttons-div{ + position: relative; +} + + +div.buttons-candidato{ + display: flex; + gap: 10px; + height: 60px; + flex-direction: column; + align-items: center; + margin: auto auto; + padding-top: 40px; + +} + +div.candidato-resumo-ia{ + display: flex; + flex-direction: column; + gap: 10px; + margin: auto; + align-items: center; + color: #b3aeae; + cursor: pointer; +} + + +span.buttons-candidato-span{ + margin-right: 2px; + color: white; + padding: 8px 25px; + cursor: pointer; + transition: all 0.3s; + font-weight: 600; + user-select: none; + width: 300px; + text-align: center; + border-radius: 5px 5px 10px 20px; +} + +span.buttons-candidato-span:hover{ + transform: translateX(10px); +} + +span#edit{ + background: var(--secondary-blue); +} + +span#aprove{ + background: var(--mineral-green); +} + +span#return{ + background: var(--secondary-red); +} + +span#reject{ + background: var(--primary-red); +} + +span#candidato-vaga{ + + text-decoration: underline; + +} + +span#candidato-status-id{ + right: 0; + position: absolute; + top: 0; + left: 0; + border-radius: 20px 20px 0 0; + color: white; + font-size: 18px; + padding: 4px; + font-weight: 500; +} + +.status-background{ + background: var(--primary-orange); +} + +.status-enviado{ + background: var(--blue-one) +} + +.status-rejeitado{ + background: var(--secondary-red); +} + +.status-entrevista{ + background: var(--purple-blue); +} + +.status-aprovado{ + background: var(--mineral-green); +} + +.form-text-area { + box-sizing: border-box; + display: block; + max-width: 88vh; + line-height: 1.5; + padding: 15px 15px 15px; + border-radius: 3px; + font-size: 15px; + width: -webkit-fill-available; +} + +.form-text-area:focus-visible { + outline: 1px solid gray; +} + + +.page-content{ + position: absolute; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 1; + overflow: hidden; + display: none; +} + +.buttons-editing-mode{ + display: flex; + position: absolute; + left: 50%; + padding: 10px; + gap: 10px; +} + +#modalPageCandidato { + align-items: center; + border: none; + gap: 10px; + justify-content: center; + position: fixed; + top: 14%; + left: 50%; + transform: translate(-50%, -50%); + padding: 10px 15px 10px 15px; + background: var(--mineral-green); + color: white; + font-weight: 500; + border-radius: 40px; + transition: all 0.3s ease; + z-index: 9999; +} + +#confirm-edit { + background: var(--mineral-green); + border: none; + padding: 5px 15px; + border-radius: 5px; + color: white; + font-weight: 500; +} + +#cancel-edit { + background: var(--secondary-red); + border: none; + padding: 5px 15px; + border-radius: 5px; + color: white; + font-weight: 500; +} + +#loading{ + position: absolute; + font-size: 150px; + z-index: 10000; + /* top: 14%; */ + left: 50%; + transform: translate(-50%); +} + +#spinner { + width: 3.25em; + transform-origin: center; + animation: rotate4 2s linear infinite; +} + +#loading-circle { + fill: none; + stroke: hsl(214, 97%, 59%); + stroke-width: 2; + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + stroke-linecap: round; + animation: dash4 1.5s ease-in-out infinite; +} + +@keyframes rotate4 { + 100% { + transform: rotate(360deg); + } +} + +@keyframes dash4 { + 0% { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + } + + 50% { + stroke-dasharray: 90, 200; + stroke-dashoffset: -35px; + } + + 100% { + stroke-dashoffset: -125px; + } +} \ No newline at end of file diff --git a/static/candidatos_vaga_id.css b/static/candidatos_vaga_id.css new file mode 100644 index 0000000000000000000000000000000000000000..92bbe4585cad4391155935a551418766711839a6 --- /dev/null +++ b/static/candidatos_vaga_id.css @@ -0,0 +1,91 @@ +.container-vaga-page { + display: grid; + position: relative; + margin: 50px auto; + background-color: var(--grey); + box-sizing: border-box; + border-radius: 10px; + width: 90%; + box-shadow: 0px 6px 5px rgba(0, 0, 0, 0.3); + +} + +.container-vaga-page-header{ + display: flex; + position: relative; + min-height: 130px; + margin: 10px; + background-color: var(--light-grey); + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; +} + + + +.vaga-info{ + position: absolute; + list-style: none; + left: 21%; + margin: 10px auto; +} + + +#vaga-cargo{ + font-size: 23px; + font-weight: 500; +} + + +#vaga-departamento{ + font-size: 20px; + font-weight: 400; +} + + + +.buttons-interact { + display: flex; + justify-content: space-around; + border: none; + position: absolute; + left: 50%; + padding: 10px; + background-color: var(--secondary-blue); + border-radius: 20px; + width: 140px; + margin-top: 10px; +} + +.buttons-interact-button { + border: none; + background-color: transparent; + color: white; + font-size: 25px; +} + + +.grid-candidatos { + min-height: 130px; + margin: 10px; + background-color: var(--light-grey); + border-top-right-radius: 10px; + border-top-left-radius: 10px; +} + +.dt-container{ + position: relative; + clear: both; + width: 100%; + padding: 30px; +} + + +table.dataTable>tbody>tr>td{ + cursor: pointer; +} +table.dataTable>tbody>tr:hover{ + background-color: var(--grey); + transition: 0.1s ease-in; +} + + diff --git a/static/edita_vaga.css b/static/edita_vaga.css new file mode 100644 index 0000000000000000000000000000000000000000..c9982d5585c8a47dc4f299d750f2ae21375313c5 --- /dev/null +++ b/static/edita_vaga.css @@ -0,0 +1,172 @@ +body { + height: 150vh; +} + +.container { + + margin-top: 40px; + max-width: 100vh; + background-color: #f2f2f2; + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + border-radius: 10px; + box-shadow: + -3px 0 5px rgba(0, 0, 0, 0.3), + 3px 0 5px rgba(0, 0, 0, 0.3); +} + + +.form-row { + display: flex; + flex-direction: row; + border-radius: 10px; + + +} + +.form-row label { + margin-right: 250px; + +} + + +.form-group { + margin-top: 20px; + margin-bottom: 20px; + margin-left: 20px; + +} + +.labelClass { + padding-bottom: 6px; + ; + font-weight: 500; + font-size: 18px; +} + +textarea { + width: 191%; + padding: 10px; + box-sizing: border-box; + +} + +.modal { + display: none; + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); +} + +.modal-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} + +label { + white-space: nowrap; +} + +.labelHeader { + display: block; + /* Garante que a label ocupe toda a largura disponível */ + font-size: 2em; + /* Tamanho de fonte semelhante ao de um h1 */ + font-weight: bold; + /* Negrito semelhante ao de um h1 */ + margin-bottom: 10px; + /* Margem inferior semelhante à de um h1 */ + color: #333; + /* Cor do texto semelhante à de um h1 */ + +} + +.form-row.with-top-border { + position: relative; +} + +.form-row.with-top-border::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 7px; + /* Ajuste a altura da linha conforme necessário */ + background-color: #23232e; + /* Defina a cor da linha */ + border-top-left-radius: 15px; + border-top-right-radius: 15px +} + +.form-button { + display: flex; + justify-content: center; + flex-direction: row; + padding: 20px; +} + +.btn.btn-primary.btn-lg { + border-radius: 20px; + width: 273px; + background-color: #005573; + border-color: #005573; + transition: background-color 0.3s ease; +} + +.btn.btn-primary.btn-lg:hover { + background-color: #00aa9b; + border-color: #005573; +} + +.btn.btn-primary.btn-lg:focus { + background-color: #005573; + border-color: #005573; +} + +textarea#descricao { + width: 88vh; + box-sizing: border-box; + display: block; + max-width: 88vh; + line-height: 1.5; + padding: 15px 15px 15px; + border-radius: 3px; + font: 15px 'Poppins', cursive; + transition: box-shadow 0.5s ease; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); +} + +.header-page-vagas>span { + color: white; + font-size: 32px; + font-weight: 700; + background: #005573; + padding: 10px 10px; + border-radius: 10px 10px 0 0; + margin: 0 0 0 5px; +} + + diff --git a/static/img/bg.jpg b/static/img/bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3bec4aa93e99e55d2d927292c324078d8a9ff82f --- /dev/null +++ b/static/img/bg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8eee3ae5d004e007a65d7c2e820cafe8eb2d47a39f693e33412f7543fc767fa1 +size 1329645 diff --git a/static/img/editar.png b/static/img/editar.png new file mode 100644 index 0000000000000000000000000000000000000000..9857e4c06a4377b310d2465375d5c5084901d20e Binary files /dev/null and b/static/img/editar.png differ diff --git a/static/img/inetum.svg b/static/img/inetum.svg new file mode 100644 index 0000000000000000000000000000000000000000..0e6cdb824fec498caf0848949699597f33848787 --- /dev/null +++ b/static/img/inetum.svg @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/img/logo-inetum.png b/static/img/logo-inetum.png new file mode 100644 index 0000000000000000000000000000000000000000..f6627bc05c0d1a72091abe8162fd6297deffa39f Binary files /dev/null and b/static/img/logo-inetum.png differ diff --git a/static/img/logo-inetum_.svg b/static/img/logo-inetum_.svg new file mode 100644 index 0000000000000000000000000000000000000000..b171bcec602dc9c933506089aaae744cd55b6199 --- /dev/null +++ b/static/img/logo-inetum_.svg @@ -0,0 +1,20 @@ + + + D213220D-2199-49E0-A4B0-9C4CE77660F9 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/img/logo.png b/static/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e05f3644ab916dc530c0eb11b91705b51acdcbe1 Binary files /dev/null and b/static/img/logo.png differ diff --git a/static/index.css b/static/index.css new file mode 100644 index 0000000000000000000000000000000000000000..889aa0800ef937321f0fb71d0614900c4cddbef1 --- /dev/null +++ b/static/index.css @@ -0,0 +1,68 @@ +.container-cards { + background-color: #f2f2f2; + position: relative; + max-width: 1200px; + margin: 50px auto; + border-radius: 10px; + box-shadow: + -3px 0 5px rgba(0, 0, 0, 0.3), + 3px 0 5px rgba(0, 0, 0, 0.3); + +} + +.card-body { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + background-color: var(--secondary-blue); + border-radius: 10px; + height: 160px; + width: 160px; + padding: 10px; + +} + +.card-body i { + font-size: 40px; + color: var(--mineral-green); + margin-bottom: 20px; + +} + +.card-body:hover { + outline: 5px solid var(--mineral-green); +} + +.cards-home-page { + display: grid; +} + + + +.cards-home-page a { + text-decoration: none; + color: #fff; +} + +.card-subtitle { + color: white; + font-weight: 600; + padding: 0 2px 0 0; + ; +} + +.header-page-main { + padding-left: 10px; +} + +.rows-data { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-auto-rows: 270px; + gap: 10px; + place-items: center; + padding-top: 50px; +} + diff --git a/static/jquery-3.7.1.min.js b/static/jquery-3.7.1.min.js new file mode 100644 index 0000000000000000000000000000000000000000..90eab5fe20f9d68e6d1f43960f297ea9f08cd5d1 --- /dev/null +++ b/static/jquery-3.7.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0=500px) { + body { + padding: 0; + } +} + +.background { + position: fixed; + top: -50vmin; + left: -50vmin; + width: 100vmin; + height: 100vmin; + border-radius: 47% 53% 61% 39% / 45% 51% 49% 55%; + background: #00AA9B; +} + +.background::after { + content: ""; + position: inherit; + right: -50vmin; + bottom: -55vmin; + width: inherit; + height: inherit; + border-radius: inherit; + background: #232d4b; +} + +.card { + overflow: hidden; + top: 124px; + position: absolute; + z-index: 3; + width: 94%; + margin: 0 20px; + padding: 170px 30px 25px; + border-radius: 24px; + background: #ffffff; + text-align: center; + box-shadow: 0 100px 100px rgb(0 0 0 / 10%); +} + +.card::before { + content: ""; + position: absolute; + top: -880px; + left: 50%; + translate: -50% 0; + width: 1000px; + height: 1000px; + border-radius: 50%; + background: #005573; +} + +@media (width >=500px) { + .card { + margin: 0; + width: 360px; + } +} + +.card .logo { + position: absolute; + top: 10px; + left: 50%; + translate: -50% 0; + width: 280px; +} + +.card>h2 { + font-size: 22px; + font-weight: 400; + margin: 0 0 38px; + color: rgb(0 0 0 / 38%); +} + +.form { + margin: 0 0 44px; + display: grid; + gap: 12px; + justify-content: space-around; +} + +.form :is(input, button) { + width: 100%; + height: 56px; + border-radius: 28px; + font-size: 16px; + font-family: inherit; +} + +.form>input { + border: 0; + padding: 0 24px; + color: #222222; + background: #ededed; +} + +.form>input::placeholder { + color: rgb(0 0 0 / 28%); +} + +.form>button { + border: 0; + color: #f9f9f9; + background: #005573; + display: grid; + place-items: center; + font-weight: 500; + cursor: pointer; + margin-top: 20px +} + +.card>footer { + color: #a1a1a1; +} + +.card>footer>a { + color: #216ce7; +} + +.title-login { + white-space: nowrap; + padding-bottom: 20px; + +} + +input.error { + border: 2px solid #f04641; +} + +input.error.fade-out { + transition: border-color 1s ease; + /* Adiciona uma transição suave */ + border-color: transparent; + /* Define a cor da borda como transparente */ +} + +/* Genel stil */ +.toggle-switch { + position: relative; + display: inline-block; + width: 40px; + height: 24px; + margin: 10px; +} + +/* Giriş stil */ +.toggle-switch .toggle-input { + display: none; +} + +/* Anahtarın stilinin etrafındaki etiketin stil */ +.toggle-switch .toggle-label { + position: absolute; + top: 0; + left: 0; + width: 40px; + height: 24px; + background-color: #005573; + border-radius: 34px; + cursor: pointer; + transition: background-color 0.3s; +} + +/* Anahtarın yuvarlak kısmının stil */ +.toggle-switch .toggle-label::before { + content: ""; + position: absolute; + width: 20px; + height: 20px; + border-radius: 50%; + top: 2px; + left: 2px; + background-color: #fff; + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + transition: transform 0.3s; +} + +/* Anahtarın etkin hale gelmesindeki stil değişiklikleri */ +.toggle-switch .toggle-input:checked+.toggle-label { + background-color: #00AA9B; +} + +.toggle-switch .toggle-input:checked+.toggle-label::before { + transform: translateX(16px); +} + +/* Light tema */ +.toggle-switch.light .toggle-label { + background-color: #BEBEBE; +} + +.toggle-switch.light .toggle-input:checked+.toggle-label { + background-color: #9B9B9B; +} + +.toggle-switch.light .toggle-input:checked+.toggle-label::before { + transform: translateX(6px); +} + +/* Dark tema */ +.toggle-switch.dark .toggle-label { + background-color: #4B4B4B; +} + +.toggle-switch.dark .toggle-input:checked+.toggle-label { + background-color: #717171; +} + +.toggle-switch.dark .toggle-input:checked+.toggle-label::before { + transform: translateX(16px); +} + +.remember-field { + display: flex; + align-items: center; + justify-content: center; +} + +.remember-label { + font-weight: 300; +} + diff --git a/static/notification.css b/static/notification.css new file mode 100644 index 0000000000000000000000000000000000000000..9fa69964a15298fdf94bfd138572ca6b5cd739a2 --- /dev/null +++ b/static/notification.css @@ -0,0 +1,133 @@ +#toastBoxInetum { + position: absolute; + bottom: -700px; + right: 1px; + display: flex; + align-items: flex-end; + flex-direction: column; + overflow: hidden; + padding: 20px; +} + + + +.toastInetum{ + display: flex; + align-items: center; + position: relative; + width: 400px; + height: 80px; + background: #fff; + font-weight: 500; + margin: 15px 0; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + transform: translateX(100%); + animation: moveleft 0.4s linear forwards; +} + +.toastInetum i{ + margin: 0 20px; + font-size: 35px; + color: var(--mineral-green); + + +} + +.toastInetum.error-proccess i{ + color: var(--primary-red); +} + +.toastInetum.warning-proccess i{ + color: #cca123; + +} + +.toastInetum::after{ + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 5px; + background: var(--mineral-green); + animation: anim 5s linear forwards; +} + +@keyframes moveleft { + 100%{ + transform: translateX(0); + } + +} + +@keyframes anim { + 100%{ + width: 0; + } + +} + +.toastInetum.error-proccess::after{ + background: var(--primary-red); +} + +.toastInetum.warning-proccess::after{ + background: #cca123; +} + + +.container-candidato{ + width: 80%; + margin: 0 auto; +} + +.tab_trigger ul{ + padding: 0; + margin: 0; + list-style: none; +} + +.tab_trigger ul li{ + display: inline-block; + margin-right: 2px; + border-radius: 12px 12px 0 0; + background-color: var(--blue-one); + color: white; + padding: 8px 25px; + cursor: pointer; + transition: all 0.3s; + font-weight: 700; +} + +.tab_trigger ul li:hover, +.tab_trigger ul li.active{ + background-color: var(--mineral-green); +} + +.tab_content_box{ + display: none; + font-size: 20px; + color: #000; + background-color: var(--grey); + padding: 25px; + border-radius: 0 12px 12px 12px; +} + +.tab_content_box:first-child{ + display: block; +} + +#modalPageCandidato{ + align-items: center; + justify-content: center; + position: fixed; + top: 14%; + left: 50%; + transform: translate(-50%, -50%); + padding: 10px 15px 10px 15px; + background: var(--mineral-green); + color: white; + font-weight: 500; + border-radius: 40px; + transition: all 0.3s ease; +} \ No newline at end of file diff --git a/static/nova_vaga.css b/static/nova_vaga.css new file mode 100644 index 0000000000000000000000000000000000000000..882583978eeeb83eb928130611f7eea97b357769 --- /dev/null +++ b/static/nova_vaga.css @@ -0,0 +1,146 @@ +body { + height: 150vh; +} + +.container { + display: grid; + position: relative; + margin-top: 40px; + max-width: 100vh; + background-color: var(--grey); + flex-wrap: wrap; + box-sizing: border-box; + border-radius: 10px; + box-shadow: + -3px 0 5px rgba(0, 0, 0, 0.3), + 3px 0 5px rgba(0, 0, 0, 0.3); +} + + +.form-row { + border-radius: 10px; + + +} + +.form-row label { + margin-right: 250px; + +} + + +.form-group { + margin: 20px 0 20px 20px; + display: grid; + gap: 2px; + padding-top: 30px; +} + +.labelClass { + padding-bottom: 6px; + font-weight: 500; + font-size: 18px; +} + + +label { + white-space: nowrap; +} + +.labelHeader { + display: block; + /* Garante que a label ocupe toda a largura disponível */ + font-size: 2em; + /* Tamanho de fonte semelhante ao de um h1 */ + font-weight: bold; + /* Negrito semelhante ao de um h1 */ + margin-bottom: 10px; + /* Margem inferior semelhante à de um h1 */ + color: #333; + /* Cor do texto semelhante à de um h1 */ + +} + +.form-row.with-top-border { + position: relative; +} + +.form-row.with-top-border::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 7px; + /* Ajuste a altura da linha conforme necessário */ + background-color: #23232e; + /* Defina a cor da linha */ + border-top-left-radius: 15px; + border-top-right-radius: 15px +} + +.form-button { + display: flex; + justify-content: center; + flex-direction: row; + padding: 20px; +} + +.btn.btn-primary.btn-lg { + border-radius: 20px; + width: 273px; + background-color: #005573; + border-color: #005573; + transition: background-color 0.3s ease; +} + +.btn.btn-primary.btn-lg:hover { + background-color: #00aa9b; + border-color: #005573; +} + +.btn.btn-primary.btn-lg:focus { + background-color: #005573; + border-color: #005573; +} + +textarea { + width: 101%; + padding: 10px; + box-sizing: border-box; + +} + +.form-text-area { + width: 70vh; + box-sizing: border-box; + display: block; + max-width: 88vh; + line-height: 1.5; + padding: 15px 15px 15px; + border-radius: 3px; + font-size: 15px; + transition: box-shadow 0.5s ease; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.3) +} + +.form-text-area:focus-visible { + outline: 1px solid gray; +} + + +#header-field { + background: var(--secondary-blue); + position: absolute; + top: 0; + left: 0; + text-align: center; + border-bottom-right-radius: 10px; + color: white; + font-weight: 700; + width: 400px; + padding: 15px; + font-size: 30px; +} + + diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000000000000000000000000000000000000..95c3c6eb034a353b6f5ef00ae5580f2fef9f3602 --- /dev/null +++ b/static/script.js @@ -0,0 +1,43 @@ +class MobileNavbar { + constructor(mobileMenu, navList, navLinks) { + this.mobileMenu = document.querySelector(mobileMenu); + this.navList = document.querySelector(navList); + this.navLinks = document.querySelectorAll(navLinks); + this.activeClass = "active"; + + this.handleClick = this.handleClick.bind(this); + } + animateLinks(){ + this.navLinks.forEach((link) => { + link.style.animation + ? (link.style.animation = "") + : (link.style.animation = 'navLinkFade 0.5s ease forwards 0.3s') + + }); + } + handleClick(){ + this.navList.classList.toggle(this.activeClass); + this.mobileMenu.classList.toggle(this.activeClass); + this.animateLinks(); + + } + addClickEvent(){ + this.mobileMenu.addEventListener("click", this.handleClick); + } + + init () { + if (this.mobileMenu) { + this.addClickEvent(); + } + return this; + } +} + +const mobileNavbar = new MobileNavbar( + ".mobile-menu", + ".nav-list", + ".nav-list li", +); +mobileNavbar.init(); + + diff --git a/static/sounds/level-down.mp3 b/static/sounds/level-down.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f4c738283fbf60e25ba301f26e842018fda7cb86 Binary files /dev/null and b/static/sounds/level-down.mp3 differ diff --git a/static/sounds/level-up.mp3 b/static/sounds/level-up.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e3666fc1c978d3c495d8806b518038b20df972fe Binary files /dev/null and b/static/sounds/level-up.mp3 differ diff --git a/static/vaga.css b/static/vaga.css new file mode 100644 index 0000000000000000000000000000000000000000..099eaaa3b8fd81f677912490101ee3d1a88805df --- /dev/null +++ b/static/vaga.css @@ -0,0 +1,356 @@ +#vaga-inativo { + margin: 0 auto; + color: white; + text-decoration: none; + background: var(--secondary-red); + padding: 10px; + font-size: 16px; + font-weight: 500; + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.3); + position: absolute; + left: 50%; + transform: translateX(-50%); + z-index: 1; + bottom: 97%; + border-radius: 5px; +} + +.grid-vaga { + margin: 0; +} + +.item-vaga { + display: grid; + position: relative; + grid-template-columns: repeat(2, 1fr); + margin: 5px; + background-color: var(--light-grey); + padding: 10px; + gap: 5px; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; +} + + + +.vaga-info { + display: flex; + flex-direction: column; + list-style-type: none; + padding-left: 0; + margin: 70px auto 25px auto; +} + +ul.vaga-info li { + text-align: justify; + padding: 5px; +} + +.grid-vaga-item { + padding: 5px; +} + + + +.button-container { + + position: relative; + display: inline-block; + +} + +.expand-button { + background-color: var(--secondary-blue); + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + padding-top: 2px; + +} + + +.container-vaga-page { + display: grid; + position: relative; + margin: 50px auto; + background-color: var(--grey); + box-sizing: border-box; + border-radius: 10px; + width: 80%; + box-shadow: 0px 6px 5px rgba(0, 0, 0, 0.3) +} + +.bi-vg { + font-size: 24px; + padding-top: 2px +} + + + + + + + + + +.buttons-interact { + display: flex; + justify-content: space-around; + border: none; + padding: 10px; + background-color: var(--secondary-blue); + border-radius: 20px; +} + +.buttons-interact-button{ + border: none; + background-color: transparent; + color: white; + font-size: 20px; +} + + +.copy-share{ + display: grid; + position: relative; + align-items: center; + justify-content: center; + max-height: 300px; +} + +.copy-to-clipboard{ + display: flex; + align-items: center; + gap: 10px; + margin: 0 10px 0 10px; +} +.bi-cp { + font-size: 30px; + cursor: pointer; +} + +.bi{ + font-size: 25px; +} + +#copiar-text { + font-size: 15px; + text-decoration: none; + text-align: justify; +} + +#copiar-text>a { + color: black; + text-align: justify; + +} + +#vaga-status { + flex: 0 0 400px; + margin: 5px auto; +} + +#cargo-field { + background: var(--secondary-blue); + position: absolute; + top: 0; + left: 0; + + text-align: center; + border-bottom-right-radius: 10px; + color: white; + font-weight: 600; + padding: 15px; + font-size: 25px; +} +#cargo-field:focus{ + outline: none; + background-color: yellow +} +.conteudo-desc { + height: 60px; + overflow: hidden; + transition: height 0.3s ease; +} + +.conteudo-desc.expandido { + height: auto; + transition: height 0.3s ease; +} + +.ver-mais { + margin: 0 auto; + bottom: 0; + cursor: pointer; + position: absolute; + background: #efefef; + color: black; + left: 0; + right: 0; + text-align: center; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + font-weight: 200; + font-size: 15px; + +} + + + + +/* */ +/* FUNIL */ +/* */ +/* */ + + +.bg-funil-contrata { + display: flex; + flex-direction: column; + background-color: var(--light-grey); +} + + +.funil-cards { + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + margin: 1rem; + gap: 80px; + padding: 40px 20px; + +} + +.card-candidato { + display: grid; + place-items: center; + position: relative; + height: 300px; + width: 220px; + background: linear-gradient(to bottom, var(--secondary-blue), var(--primary-blue)); + border-radius: 10px; + cursor: pointer; + overflow: hidden; + + +} + + +.card-candidato::before { + position: absolute; + content: ""; + width: 50%; + height: 180%; + background: var(--mineral-green); + transform: rotate(45deg); +} + +.card-candidato:hover::before { + animation: animate-card 2s linear infinite; +} + +@keyframes animate-card { + from { + transform: rotate(0deg); + + } + + to { + transform: rotate(360deg); + } +} + +.card-candidato::after { + position: absolute; + content: ''; + inset: 4px; + background: linear-gradient(to bottom, var(--secondary-blue), var(--primary-blue)); + border-radius: 8px; +} + +.card-text { + margin-top: 20px; + color: white; + font-weight: 300; + font-size: 17px; + pointer-events: none; + user-select: none; + z-index: 10; + +} + +.card-number { + color: white; + font-weight: 300; + font-size: 90px; + pointer-events: none; + user-select: none; + z-index: 10; + margin-bottom: 10px; +} + +#funil-field{ + background: var(--secondary-blue); + position: absolute; + text-align: center; + border-bottom-right-radius: 10px; + color: white; + font-weight: 500; + padding: 10px; + font-size: 18px; +} + +/* */ +/* PUBLICAR */ +/* */ +/* */ + + + + +.vaga-publish { + display: grid; + align-items: center; + margin-top: auto; + margin-bottom: auto; + background-color: var(--light-grey); + margin: 5px; + padding: 10px; + justify-content: center; + align-items: center; + gap: 1rem; + +} + +.vaga-publish-text { + text-align: center; +} + +.publish-text{ + text-align: center; + font-weight: 500; +} + +.vaga-publish-buttons{ + display: flex; + align-items: center; + gap: 3rem; +} + +.bi-publish { + border: none; + font-size: 30px; +} + + +.btn-share { + border: none; + background: none; + +} + + + diff --git a/static/vagas.css b/static/vagas.css new file mode 100644 index 0000000000000000000000000000000000000000..263699c8df197c9760357cab1efea7f2dbc36225 --- /dev/null +++ b/static/vagas.css @@ -0,0 +1,236 @@ +body { + height: 115vh; +} + +.container-vagas { + + margin: 50px auto; + padding: 50px; + position: relative; + background-color: var(--grey); + box-sizing: border-box; + border-radius: 10px; + width: 80%; + box-shadow: + -3px 0 5px rgba(0, 0, 0, 0.3), + 3px 0 5px rgba(0, 0, 0, 0.3); + place-content: center; + +} + +.header-page-vagas { + padding-left: 10px; +} + + + +.card-column { + padding-top: 86px +} + +.filter-head { + list-style: none; + display: flex; + position: absolute; + width: 60%; + left: 30%; + top: 0; + margin-top: 10px; + background: linear-gradient(to left, var(--secondary-blue), var(--blue-one));; + padding: 10px; + color: white; + border-radius: 40px; + align-items: center; + user-select: none; +} + +.filter-head li { + display: flex; + position: relative; + align-items: center; + gap: 10px; + justify-content: center; + margin: 0 auto; +} + + +.dropdown-funnel { + display: inline-block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + } + + +.dropdown-menu { + display: none; + position: absolute; + top: 100%; + left: 0; + background-color: #f9f9f9; + min-width: 100px; + padding: 5px; + border: 1px solid #ccc; + white-space: nowrap; + } + + + + +.dropdown-menu li { + display: flex; + padding: 5px 10px 6px 10px; + cursor: pointer; + justify-content: space-around; + + +} + +.dropdown{ + width: 200px; +} + +i{ + font-size: 30px; +} + + +.filter-content{ + display: flex; + align-items: center; + gap: 20px; +} +#status-dropdown{ + width: auto; + text-align: center; + min-width: 120px; + height: 26px; + border: none; + opacity: 0.9; +} + +#status-dropdown:focus-visible{ + outline: 1px solid var(--grey); +} + +.card-container { + margin: 10px auto; + display: grid; + grid-template-columns: repeat(2, 1fr); + padding: 40px; +} + +.card-content { + padding-bottom: 20px; + margin: 10px; + background-color: white; + border-radius: 0 0 10px 0; + width: 44%; + user-select: none; +} + + +.card-content:hover { + transition: all 0.3s; + transform: translateX(10px); + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3); +} + +.status-bar { + height: 5px; + width: 100%; +} + +.ativo { + border-top-right-radius: 10px; + background: linear-gradient(to right, var(--blue-one) 65%, var(--mineral-green) 90%); +} + +.inativo { + border-top-right-radius: 10px; + background: linear-gradient(to right, var(--blue-one) 65%, #f04641 90%); +} + +.div-card-title { + display: grid; + grid-template-columns: 2fr 1fr; + justify-items: start; + padding: 5px 10px; +} + +.card-data-publicacao{ + padding: 5px 10px; +} + + + + +.add-vaga-button { + background-color: var(--secondary-blue); + color: white; + border: none; + border-radius: 5px; + padding: 10px 20px; + font-size: 16px; + cursor: pointer; +} + +/* Estilos para o botão quando hover */ +.add-vaga-button:hover { + background-color: #004a5e; +} + +.header-filter { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + margin-top: 50px; +} + +#filtro-status { + max-width: 250px; +} + + +.hidden{ + display: none + +} + +.container-input { + position: relative; +} + +.input-search { + width: 150px; + padding: 10px 0px 10px 40px; + border-radius: 9999px; + border: solid 1px #333; + transition: all .2s ease-in-out; + outline: none; + background-color: linear-gradient(to bottom, #f9f9f9, #ccc); + opacity: 0.8; +} + +.container-input svg { + position: absolute; + top: 50%; + left: 10px; + transform: translate(0, -50%); +} + +.input-search:focus { + opacity: 0.8; + width: 300px; +} + +.icons-filter { + display: flex; + gap: 60px +} + + +#not-found{ + color: black; +} \ No newline at end of file diff --git a/templates/aba_candidato.html b/templates/aba_candidato.html new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/templates/aba_candidato.html @@ -0,0 +1 @@ + diff --git a/templates/animation_publish.html b/templates/animation_publish.html new file mode 100644 index 0000000000000000000000000000000000000000..8d74f3b8ea91762f48fb2f23cd25272e85369f66 --- /dev/null +++ b/templates/animation_publish.html @@ -0,0 +1,175 @@ + + +
+ + + + diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000000000000000000000000000000000000..95d005bbfbe794f1af90deaa3067e77433668ac5 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + {% block head %} {% endblock %} + Portal RH + + +
+
+ + + +
+ + {% block conteudo %} {% endblock %} {% block finalscript %} {% endblock %} + + diff --git a/templates/candidatos.html b/templates/candidatos.html new file mode 100644 index 0000000000000000000000000000000000000000..186c49ab6bc3d6452ca7400ff3b9a1fac171e850 --- /dev/null +++ b/templates/candidatos.html @@ -0,0 +1,393 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} +
+ +
+
+
+
+ Candidatos +
+
+
    +
  • +
    + +
    +
  • +
    + + +
    +
  • +
    + + +
    +
  • +
  • +
    + + + + +
    +
  • +
+
+ {% if candidatos|length == 0 %} +
+ + Nenhum candidato disponível +
+ {% endif %} + +
+
+{% include 'animation_publish.html' %} {% endblock %} {% block finalscript %} + + + + + + + + + +{%endblock %} diff --git a/templates/candidatos_candidato_id.html b/templates/candidatos_candidato_id.html new file mode 100644 index 0000000000000000000000000000000000000000..9245fe17e5d0625c348ddcbccb13fab7468bebd5 --- /dev/null +++ b/templates/candidatos_candidato_id.html @@ -0,0 +1,480 @@ +{% extends 'base.html' %} {% block head %} + + +{% endblock %} {% block conteudo %} + + + + + + +
+
+ + +
+
+
+
+
+
+ Candidato +
+
+
+
+ {{ candidato.status }} + + {{ candidato.nome }} + + {{ candidato.vaga.cargo | upper }} + + + {{ candidato.telefone }} + {{ candidato.email }} +
+
+ Editar candidato + Aprovar para próxima fase + Voltar à fase anterior + Reprovar candidato +
+
+
+
+
    +
  • Experiência
  • +
  • Ensino
  • +
  • Certificados/Conhecimentos
  • +
  • Resumo
  • +
+
+ +
+
+ {% for exp in candidato.experiencia %} +
+

Cargo

+ +

Empresa

+ +

Data de início

+ +

Data de término

+ +

Descrição

+ +
+ {% endfor %} +
+
+ {% for formacao in candidato.educacao %} +
+

Formação

+ +

Instituição

+ +

Data de Conclusão

+ +
+ + {% endfor %} +
+
+ {% for hab in candidato.habilidades %} +
+

Skill

+ +
+ + {% endfor %} +
+
+ {% if candidato.resumo_ia %} +
+ {{ candidato.resumo_ia }} +
+ Avaliação: {{ candidato.avaliacao_ia }} +
+ {% else %} +
+ Clique para gerar o resumo + +
+ {% endif %} +
+
+
+
+
+
+
+ +{% endblock %} {% block finalscript %} + + + + + + + + + +{% endblock %} diff --git a/templates/candidatos_vaga_id.html b/templates/candidatos_vaga_id.html new file mode 100644 index 0000000000000000000000000000000000000000..409eba381fd408cc82dc9b56f6566f2c02b840f0 --- /dev/null +++ b/templates/candidatos_vaga_id.html @@ -0,0 +1,99 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} + +
+
+
+ Candidatos +
    +
  • {{ vaga.cargo }} {{vaga.nivel}}
  • +
  • {{vaga.departamento}}
  • +
  • + Vaga aberta em {{vaga.data_abertura.strftime('%d/%m/%Y')}} +
  • +
+ +
+ +
+
+ +
+ + + + + + + + + + + {% for candidato in candidatos %} {% if candidato.nome != none %} + + + + + + + {% endif %} {% endfor %} + +
NomeEmailTelefoneStatus
{{candidato.nome}}{{candidato.email}}{{candidato.telefone}}{{candidato.status}}
+
+
+ {% include 'animation_publish.html' %} +
+ +{% endblock %} {% block finalscript %} + + + + + +{% endblock %} diff --git a/templates/editar_vaga.html b/templates/editar_vaga.html new file mode 100644 index 0000000000000000000000000000000000000000..c531b9e37680fb73ad106480acb2d17223c95bec --- /dev/null +++ b/templates/editar_vaga.html @@ -0,0 +1,138 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} + +
+
+ Editar vaga +
+ +
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+ + +
+
+
+
+{% endblock %} {% block finalscript %} + +{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000000000000000000000000000000000000..9d5db75b86f2152d650619ff90e0540788f3d147 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,99 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} + + +{% endblock %} diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000000000000000000000000000000000000..0402d995509be2111af831bbbe5558d12258c998 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,87 @@ + + + + + + + + + + + Portal RH + + + +
+
+ + +
+ + + +
+ +
+ + + +
+
+ +
+
+ + + + + diff --git a/templates/new_user.html b/templates/new_user.html new file mode 100644 index 0000000000000000000000000000000000000000..9d61ee12350e84606623ec538fc3522a021e4a6d --- /dev/null +++ b/templates/new_user.html @@ -0,0 +1,172 @@ + +{% extends 'base.html' %} + +{% block conteudo %} + + + + + +
+ +
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + +
+ + + + + + + + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/notification.html b/templates/notification.html new file mode 100644 index 0000000000000000000000000000000000000000..9368a999639deb56859ad24c88c5ca0c74644732 --- /dev/null +++ b/templates/notification.html @@ -0,0 +1,81 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} + + + +
+
+ + + + +
+
+
+ +{% endblock %} {% block finalscript %} + + +{% endblock %} diff --git a/templates/nova_candidatura.html b/templates/nova_candidatura.html new file mode 100644 index 0000000000000000000000000000000000000000..df3cb0e3c2229fe426513d774cb188350a31ead6 --- /dev/null +++ b/templates/nova_candidatura.html @@ -0,0 +1,244 @@ + +{% extends 'base.html' %} + +{% block conteudo %} + + + + +
+ +
+
+
+
+ + +
+
+ + + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + + +
+
+ +
+
+ + +
+
+ +
+
+ + + + + +
+
+ +
+ + +
+ +
+ +
+ +
+
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/templates/nova_vaga.html b/templates/nova_vaga.html new file mode 100644 index 0000000000000000000000000000000000000000..300c5f502145ed3001f6a0dca750f529fb16ef92 --- /dev/null +++ b/templates/nova_vaga.html @@ -0,0 +1,132 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} + +
+
+ Crie uma nova vaga +
+ +
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + + +
+
+ +
+
+ + +
+
+ +
+ +
+
+
+
+{% endblock %} {% block finalscript %} + + +{% endblock %} diff --git a/templates/nova_vaga_test.html b/templates/nova_vaga_test.html new file mode 100644 index 0000000000000000000000000000000000000000..26b0389c203294c56efe00c263a3a7d2f01bcebd --- /dev/null +++ b/templates/nova_vaga_test.html @@ -0,0 +1,185 @@ +{% extends 'base.html' %} +{% block conteudo %} + + + + + + + +
+ + + + +
+
+
+
+
+

Slide 1

+

Content for Slide 1

+
+
+
+
+

Slide 2

+

Content for Slide 2

+
+
+
+
+

Slide 3

+

Content for Slide 3

+
+
+
+
+

Slide 4

+

Content for Slide 4

+
+
+
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/users.html b/templates/users.html new file mode 100644 index 0000000000000000000000000000000000000000..59b64e7e2363962ce95e0b6568d9a0913135d4cb --- /dev/null +++ b/templates/users.html @@ -0,0 +1,89 @@ + +{% extends 'base.html' %} + +{% block conteudo %} + + + + + +

Usuários

+ +
+ + + + + + + + + + + + + + + + {% for user in users %} + + + + + + + + + + {% endfor %} + + + + +
NomeSobrenomeEmailTipo
{{user.nome}}{{user.sobrenome}}{{user.email}}{{user.user_type}}
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/vaga.html b/templates/vaga.html new file mode 100644 index 0000000000000000000000000000000000000000..4a8ae1b2fb2286b275fcd84a95ac482a70b0242d --- /dev/null +++ b/templates/vaga.html @@ -0,0 +1,329 @@ +{% extends 'base.html' %} {% block head %} + + +{% endblock %} {% block conteudo %} + + +
+
+ {% if vaga.status == 'inativo' %} +
+ Vaga desativada +
+ {% endif %} +
+
+
+
+ {{vaga.cargo}} {{vaga.nivel}} + +
    +
  • + Data de criação:
    + {{vaga.data_abertura.strftime('%d/%m/%Y')}} +
  • +
  • + Departamento:
    + {{vaga.departamento}} +
  • +
  • + Localização:
    + {{vaga.local}} +
  • +
    +
  • + Escopo da vaga:
    + {{vaga.escopo_vaga}} +
  • +
  • + Requisitos:
    + {{vaga.requisitos}} +
  • +
  • + Qualificações e certificados:
    + {{vaga.qualificacoes}} +
  • + {% if vaga.info_adicionais %} +
  • + Informações adicionais:
    + {{vaga.info_adicionais}} +
  • + {% endif %} +
    +
+
+ expand_more + +
+
+ + + {% if vaga.status == 'ativo' %} + + {% else %} + + {% endif %} +
+
+ + Copiar template para área de + transferência +
+
+
+ Publique a vaga +
+
+ + + + + +
+
+
+
+
+ +
+
+ Funil de contratação + +
+
+

Currículos enviados

+

+ {{ count_status(candidatos, 'Currículo enviado') }} +

+
+ +
+

Background check

+

+ {{ count_status(candidatos, 'Background check') }} +

+
+ +
+

Entrevista técnica

+

+ {{ count_status(candidatos, 'Entrevista técnica') }} +

+
+ +
+

Aprovados

+

+ {{ count_status(candidatos, 'Aprovado') }} +

+
+
+
+
+
+
+ {% include 'animation_publish.html' %} +
+{% endblock %} {% block finalscript %} + + + + + + +{% endblock %} diff --git a/templates/vagas.html b/templates/vagas.html new file mode 100644 index 0000000000000000000000000000000000000000..b50fe6c6273fb0e6af5e84670aaff94657293ec6 --- /dev/null +++ b/templates/vagas.html @@ -0,0 +1,342 @@ +{% extends 'base.html' %} {% block head %} + +{% endblock %} {% block conteudo %} +
+
+
+ Central de vagas +
+
+
    +
  • + + + +
  • +
    + + +
    +
  • +
    + + +
    +
  • +
  • +
    + + + + +
    +
  • +
+
+
+ + {% for vaga in vagas %} + +
+
+
+
{{vaga.cargo}} | {{vaga.nivel}}
+

{{vaga.departamento}}

+
+

+ Publicado em {{vaga.data_abertura.strftime('%d/%m/%Y')}} +

+
+ + {% endfor %} +
+
+
+ +{% endblock %} {% block finalscript %} + + + + + + + + + + + + + + +{% endblock %}