Snippets Collections
=CONCATENATE(A2,
MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",(
IFERROR(IF(FIND(MID(A2,1,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,1,0),0)
+IFERROR(IF(FIND(MID(A2,2,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,2,0),0)
+IFERROR(IF(FIND(MID(A2,3,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,4,0),0)
+IFERROR(IF(FIND(MID(A2,4,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,8,0),0)
+IFERROR(IF(FIND(MID(A2,5,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,16,0),0)
+1),1),
MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",(
IFERROR(IF(FIND(MID(A2,6,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,1,0),0)
+IFERROR(IF(FIND(MID(A2,7,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,2,0),0)
+IFERROR(IF(FIND(MID(A2,8,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,4,0),0)
+IFERROR(IF(FIND(MID(A2,9,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,8,0),0)
+IFERROR(IF(FIND(MID(A2,10,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,16,0),0)
+1),1),
MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",(
IFERROR(IF(FIND(MID(A2,11,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,1,0),0)
+IFERROR(IF(FIND(MID(A2,12,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,2,0),0)
+IFERROR(IF(FIND(MID(A2,13,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,4,0),0)
+IFERROR(IF(FIND(MID(A2,14,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,8,0),0)
+IFERROR(IF(FIND(MID(A2,15,1),"ABCDEFGHIJKLMNOPQRSTUVWXYZ")>0,16,0),0)
+1),1))
function sp_site_home_url() {
 return 'https://milborpmc.pl/';
}
add_filter( 'sp_site_home_url', 'sp_site_home_url' );
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using EvoDbManager;

public class FruitHarvesting : MonoBehaviour
{
    [System.Serializable]
    public class FruitGroup
    {
        public List<GameObject> fruitList;
        private List<Vector3> initialFruitSizes = new List<Vector3>();

        public List<Vector3> InitialFruitSizes { get { return initialFruitSizes; } set { initialFruitSizes = value; } }
    }

    //Amount of times we need to click on the tree before we can harvest the fruit
    public int HarvestShakesRequired = 3;

    public bool UseTestTime = false;

    //Test wait time in seconds
    public float TestWaitTime = 10;

    public ParticleSystem LeafParticleSystem;
    public Sprite FruitSprite;

    //Tracks if we are able to shake the tree
    //Is true by default. Set to false while the tree is already shaking
    private bool _canShake = true;

    //Tracks completed shakes during harvesting
    private int _harvestShakesCompleted = 0;

    //Tracks if we are able to harvest the tree
    //Is false by default, only true when the timer has reached 0 and the fruit can be harvested
    private bool _canHarvest = false;

    public string EnvironmentItemID;

    public List<FruitGroup> FruitGroups;
    private int currentFruitGroupIndex = 0;

    //List that stores initial y values of harvestable fruit
    //so we can reset the y value after the fruit falls
    private List<float> harvestableFruitInitialY;

    private SpriteRenderer spriteRenderer;
    MaterialPropertyBlock mpb;
    float outlineAlpha = 0;
    Sequence outlinePulseSequence;

    EnvironmentItem_Logic environmentItem_Logic;
    float timeLeftToGrow;

    List<float> percentagesComplete;

    bool updateOutlineShader = false;

    private EVODbEnvironmentItem dbEnvironmentItem;
    private float waitTime;

    //Set up everything and run GrowFruit coroutine so fruit starts growing right away
    private void Start()
    {
        dbEnvironmentItem = EVODbManager.Shared.environmentItems[EnvironmentItemID];

        EnvironmentItemID = StringHelper.ReplaceWhitespace(EnvironmentItemID, "");
        environmentItem_Logic = new EnvironmentItem_Logic();

        if(UseTestTime && GameEnvironmentInfo.IsEditorOrDevelopmentBuild())
        {
            waitTime = TestWaitTime;
        }

        else
        {
            waitTime = (float)dbEnvironmentItem.waitTime;
        }

        //Start logic, will use wait time later for tweening
        environmentItem_Logic.Start(dbEnvironmentItem.uniqueId, waitTime);

        spriteRenderer = this.GetComponent<SpriteRenderer>();
        mpb = new MaterialPropertyBlock();
        spriteRenderer.GetPropertyBlock(mpb);

        harvestableFruitInitialY = new List<float>();

        percentagesComplete = new List<float>();

        for (int i = 0; i < FruitGroups.Count; i++)
        {
            SetInitialSizes(FruitGroups[i]);

            if (i == FruitGroups.Count - 1)
            {
                //Set inital positions for only the last group of fruit (The harvesteable fruit),
                //as this is the only fruit that will fall and will need it's position reset.
                SetInitialFruitPosition(FruitGroups[i].fruitList);
            }
        }

        StartFruitGrowing();
    }

    void SetInitialSizes(FruitGroup fruitGroup)
    {
        for (int i = 0; i < fruitGroup.fruitList.Count; i++)
        {
            fruitGroup.InitialFruitSizes.Add(fruitGroup.fruitList[i].transform.localScale);
        }
    }

    private void StartFruitGrowing()
    {
        double timeLeft = environmentItem_Logic.SetLastInteraction_TotalSeconds();

        if (timeLeft < 0)
        {
            timeLeft = 0;
        }

        //First we calculate the percentage of time we have left to wait
        //(time remaining / total time to wait) * 100
        float percentageLeft = (Convert.ToSingle(timeLeft / waitTime)) * 100;

        //We get the inverse by doing 100 - time remaining
        //so we have the percentage amount of time that has passed so far.
        float percentageCompleted = 100 - percentageLeft;

        //We calculate the percentage out of 100 of each fruit group
        //Normally we have 4 fruit groups so 100 / 4 = 25
        float oneGroupPercent = 100 / FruitGroups.Count;

        //Loops through all of the object lists
        for (int i = 0; i < FruitGroups.Count; i++)
        {
            percentagesComplete.Add(percentageCompleted);

            //Get min and max percentage of each fruit group
            float min = oneGroupPercent * i;
            float max = min + oneGroupPercent;

            //If our percentage completed is within the min and max of this fruit group
            //It means this is the fruit group that is currently growing and therefore we set its
            //size based on the percentWithinRange
            if (percentageCompleted > min && percentageCompleted <= max)
            {
                //Here we get the percentage out of 100 that the percentage completed is within each fruit groups min/max range
                //((input - min) * 100) / (max - min)
                //Example: If our time completed is 15%, we calculate ((15 - 0) * 100) / (25 - 0) = 60%
                //We can then use this percentage to calculate the size the fruit in the group should be
                //with 0% being at a size of 0, and 100% being at its largest size
                float percentWithinRange = ((percentageCompleted - min) * 100) / (max - min);
                SetInitialFruitSizes(FruitGroups[i], percentWithinRange);
                currentFruitGroupIndex = i;
            }

            //All other fruits are not growing and should therefore not be showing,
            //so we set their sizes to 0
            else
            {
                SetInitialFruitSizes(FruitGroups[i], 0);
            }
        }

        timeLeftToGrow = Convert.ToSingle(timeLeft);
        StartCoroutine(GrowFruit());
    }

    //Save list of initial fruit sizes so we know what their end size should be when scaling them up
    private void SetInitialFruitSizes(FruitGroup fruitGroup, float scale)
    {
        for (int i = 0; i < fruitGroup.fruitList.Count; i++)
        {
            //If we pass in a scale of zero, set scale to zero with no special logic
            if (scale == 0)
            {
                fruitGroup.fruitList[i].transform.localScale = new Vector3(0, 0, 1);
            }

            //Otherwise, we will calculate the fruit's size based on the percentage we pass in
            else
            {
                //Here we calculate each fruits size based on percentWithinRange and the fruits maximum/starting size
                //Value = percentage * max / 100
                //Example: If our fruit is 60% grown, then our fruit size would be 60 * .5f / 100 = .3f,
                //so .3 is 60% in a range of 0-.5f
                float currentScaleValueX = scale * fruitGroup.fruitList[i].transform.localScale.x / 100;
                float currentScaleValueY = scale * fruitGroup.fruitList[i].transform.localScale.y / 100;

                fruitGroup.fruitList[i].transform.localScale = new Vector3(currentScaleValueX, currentScaleValueY, 1);
            }
        }
    }

    //Save list of initial harvesteable fruit y positions so we can reset them later after they fall
    private void SetInitialFruitPosition(List<GameObject> objList)
    {
        for (int i = 0; i < objList.Count; i++)
        {
            harvestableFruitInitialY.Add(objList[i].transform.localPosition.y);
        }
    }

    //Used to loop through objects and call a function for each object.
    //We pass in a tween function to be run
    private void TweenFruitGroup(Func<GameObject, float, Tween> TweenFunction, FruitGroup fruitGroup, Sequence s, float duration)
    {
        for (int i = 0; i < fruitGroup.fruitList.Count; i++)
        {
            s.Join(TweenFunction.Invoke(fruitGroup.fruitList[i], duration));
        }
    }

    //Override with object sizes
    private void TweenFruitGroup(Func<Vector2, GameObject, float, Tween> TweenFunction, FruitGroup fruitGroup, Sequence s, float duration)
    {
        for (int i = 0; i < fruitGroup.fruitList.Count; i++)
        {
            s.Join(TweenFunction.Invoke(fruitGroup.InitialFruitSizes[i], fruitGroup.fruitList[i], duration));
        }
    }

    //Reset all fruits after you harvest them
    private void ResetFruit(FruitGroup fruitGroup)
    {
        bool resetY = false;

        //If this is the last fruit group (which is the harvesteable group), reset the y pos for that group
        if (FruitGroups.IndexOf(fruitGroup) == FruitGroups.Count - 1)
        {
            resetY = true;
        }

        for (int i = 0; i < fruitGroup.fruitList.Count; i++)
        {
            if (resetY)
            {
                //Reset y pos back to initial
                Vector3 pos = Vector3.zero;
                pos.x = fruitGroup.fruitList[i].transform.localPosition.x;
                pos.y = harvestableFruitInitialY[i];
                pos.z = fruitGroup.fruitList[i].transform.localPosition.z;

                fruitGroup.fruitList[i].transform.localPosition = pos;
            }

            //Reset size to zero
            fruitGroup.fruitList[i].transform.localScale = Vector3.zero;

            //Reset sprite alpha to 1
            SpriteRenderer sRenderer = fruitGroup.fruitList[i].GetComponent<SpriteRenderer>();
            Color c = sRenderer.color;
            c.a = 1;

            sRenderer.color = c;
        }
    }

    //Coroutine where we "grow" the fruits by scaling them based on the environment item's wait time
    private IEnumerator GrowFruit()
    {
        //Create new Last fruit group list that will hold the previous list
        FruitGroup LastFruitGroup = null;

        //Loop through all object lists and call tween logic for each list
        //pass in our sequence so we can append/join tweens to our sequence
        for (int i = currentFruitGroupIndex; i < FruitGroups.Count; i++)
        {
            //Create a new sequence. We will use this sequence for all of the object scale tweens
            //so we control when the objects tween and what to do when they are all finished tweening.
            Sequence growFruitSequence = DOTween.Sequence();
            currentFruitGroupIndex = i;

            if (LastFruitGroup != null)
            {
                TweenFruitGroup(TweenHelper.FadeOut, LastFruitGroup, growFruitSequence, 1.3f);
            }

            float timeCompleted = Convert.ToSingle(waitTime - timeLeftToGrow);
            float timeToCompleteOneGroup = Convert.ToSingle(waitTime) / FruitGroups.Count;

            //Get min and max time to grow for each fruit group
            float min = timeToCompleteOneGroup * i;
            float max = min + timeToCompleteOneGroup;

            TweenFruitGroup(TweenHelper.Scale, FruitGroups[i], growFruitSequence, max - timeCompleted);

            //Set last fruit group 
            LastFruitGroup = FruitGroups[i];

            //If next index is = to the count, we are currently on our last group
            if (i + 1 == FruitGroups.Count)
            {
                //After all of our tweens are finished scaling,
                //we set our _canHarvest bool to true which means we are now able to start clicking on the tree to harvest it
                growFruitSequence.OnComplete(() =>
                {
                    //Create the outline pulsing in and out effect
                    //This will loop infinitely until the sequence is killed after the tree has been harvested
                    CreateOutlineTween();
                    updateOutlineShader = true;
                    _canHarvest = true;
                });
            }

            //Wait for all objects in current group to finish tweening
            //Once finished we can continue the loop and move onto the next group list
            yield return growFruitSequence.WaitForCompletion();
        }
    }

    void CreateOutlineTween()
    {
        //Create the outline pulsing in and out effect
        //This will loop infinitely until the sequence is killed after the tree has been harvested
        outlinePulseSequence = DOTween.Sequence();
        outlinePulseSequence.SetAutoKill(false);

        outlinePulseSequence.Append(DOTween.To(() => outlineAlpha, x => outlineAlpha = x, 1, .6f).SetEase(Ease.InQuad));
        outlinePulseSequence.AppendInterval(.12f);

        outlinePulseSequence.Append(DOTween.To(() => outlineAlpha, x => outlineAlpha = x, 0, .6f).SetEase(Ease.InQuad));
        outlinePulseSequence.AppendInterval(.12f);

        outlinePulseSequence.OnComplete(() => outlinePulseSequence.Restart());
    }

    //ShakePlant is called by the OnTouch event triggered by the DistanceTouchEventTrigger script
    //When we are allowed to tap on the item based on our distance from it, this function is called
    public void ShakePlant()
    {
        //The ShakePlant function is run from the OnTouch() event handler in the DistanceTouchEventTrigger script
        //Only run shake logic when the tweens aren't currently running
        if (_canShake)
        {
            //Can not shake the tree while fruits are tweening
            _canShake = false;

            if (_canHarvest)
            {
                InputManager.Instance.DisableRigidbodyMovement();
                _ = QuestionSceneLoader.LoadQuestionScene(DoPostQuestionLogic, QuestionContext.Vegetation);
            }

            else
            {
                Sequence shakeSequence = DoShakeTweenSequence();
                shakeSequence.OnComplete(() =>
                {
                    //If fruit is not falling to the ground and this is just a transition tween,
                    //player can shake tree again after tweening is finished
                    _canShake = true;
                });
            }
        }
    }

    private Sequence DoShakeTweenSequence()
    {
        //Add tweens to tween sequence
        Sequence shakeSequence = TweenHelper.ShakeSequence(this.gameObject, .8f, new Vector2(.02f, .02f), 3);

        LeafParticleSystem.Play();
        //Shake tree

        //Tween fruit shake for all the current group
        TweenFruitGroup(TweenHelper.ShakeRot, FruitGroups[currentFruitGroupIndex], shakeSequence, .8f);

        return shakeSequence;
    }

    void DoPostQuestionLogic(QuestionAnswerInfo callback)
    {
        DoShakeTweenSequence();

        InputManager.Instance.EnableRigidbodyMovement();

        //increment the amount of shakes we have completed so far
        _harvestShakesCompleted++;

        //Check for which TweenFall we should run
        CheckFruitFall();
    }

    //Set Fruit fall logic for harvesting based on if this is our final shake or not
    void CheckFruitFall()
    {
        Sequence fruitFallSequence = DOTween.Sequence();

        //if we are not on our final shake,
        //tween the fruits without the fall to ground effect
        if (_harvestShakesCompleted != HarvestShakesRequired)
        {
            //Tween fruit small fall for all the current group
            TweenFruitGroup(TweenHelper.SmallFall, FruitGroups[currentFruitGroupIndex], fruitFallSequence, .5f);
            fruitFallSequence.OnComplete(() =>
            {
                _canShake = true;
            });
        }

        //if we ARE on our final shake,
        //tween the fruits WITH the fall to ground effect
        else
        {
            //Tween fruit fall to ground for all the current group
            TweenFruitGroup(TweenHelper.GroundFall, FruitGroups[currentFruitGroupIndex], fruitFallSequence, .4f);

            //after fruit falls to the group, show rewards
            fruitFallSequence.OnComplete(() =>
            {
                NavigationUIController.Instance.ShowVegetationRewardsUI(environmentItem_Logic, dbEnvironmentItem, FruitSprite);
                RestartTreeHarvesting();
            });
        }
    }

    //Restart logic to restart everything after you have finished harvesting
    void RestartTreeHarvesting()
    {
        //Tree has been harvested and apples have fallen to ground
        //reset _canHarvest bool and reset values so fruit can start growing again
        _canHarvest = false;

        _harvestShakesCompleted = 0;

        outlinePulseSequence.OnComplete(() => updateOutlineShader = false);
        outlinePulseSequence.SetAutoKill(true);

        //Fade out apples on ground
        Sequence fadeOutSequence = DOTween.Sequence();
        TweenFruitGroup(TweenHelper.FadeOut, FruitGroups[currentFruitGroupIndex], fadeOutSequence, 1.3f);

        //After apples have faded out, set their positions back to their initial positions,
        //set all groups scale back to zero, and set all groups alpha back to 1. This gets them ready for growing again
        fadeOutSequence.OnComplete(() =>
        {
            _canShake = true;

            //Loop through all groups
            for (int i = 0; i < FruitGroups.Count; i++)
            {
                //Reset y pos of current group (harvestable group)
                //Reset sizes and alpha of all items in all groups
                ResetFruit(FruitGroups[i]);
            }

            environmentItem_Logic.UpdateInteractionTime();

            StartFruitGrowing();
        });
    }

    private void Update()
    {
        if (updateOutlineShader)
        {
            UpdateOutlineAlpha();
        }
    }

    //Set alpha property of outline in our shader to the alpha we are tweening to create a phasing effect
    void UpdateOutlineAlpha()
    {
        mpb.SetFloat("_OutlineAlpha", outlineAlpha);
        spriteRenderer.SetPropertyBlock(mpb);
    }
}
 Patrón (\w+): Significa "una o más palabras" (letras, números o guiones bajos).
 Patrón (\d+): Significa "uno o más dígitos" (números).         
1. Configuración Básica (web.php)
Este es el fragmento de código inicial que debe estar presente en el array 'components' del archivo de configuración para activar el sistema de URLs bonitas:
// En web.php, dentro de 'components'
'components' => [
    'urlManager' => [
        // Habilita URLs amigables
        'enablePrettyUrl' => true,
        
        // Oculta index.php
        'showScriptName' => false,
        
        // Desactiva el análisis estricto por defecto
        'enableStrictParsing' => false,
        
        'rules' => [
            // Tus reglas personalizadas aquí
        ],
    ],
],
2. Reglas Simples y con Parámetros
Las reglas se definen dentro del array 'rules' y mapean una URL externa a una ruta interna (Controlador/Acción).
'rules' => [
    // 1. URL simple (Ejemplo: http://ejemplo.com/posts)
    'posts' => 'post/index',
    
    // 2. URL con un parámetro numérico para ID (Ejemplo: http://ejemplo.com/post/123)
    // El patrón \d+ asegura que el parámetro <id> sea un número.
    'post/<id:\d+>' => 'post/view',
    
    // 3. URL con slugs (letras, números y guiones) (Ejemplo: http://ejemplo.com/articulo/mi-titulo-genial)
    'articulo/<slug:[-a-zA-Z0-9]+>' => 'post/view',
    
    // 4. Múltiples parámetros (Ejemplo: http://ejemplo.com/categoria/5/post/101)
    'categoria/<catId:\d+>/post/<postId:\d+>' => 'post/view',
    
    // 5. Fechas (Año de 4 dígitos y Mes de 2 dígitos) (Ejemplo: http://ejemplo.com/noticias/2024/05) 
    'noticias/<year:\d{4}>/<month:\d{2}>' => 'news/archive',
    
    // 6. Reglas para parámetros opcionales (Para paginación)
    'blog/<page:\d+>' => 'post/index',
    'blog' => 'post/index',
],
3. Reglas Avanzadas y de Redirección
Este ejemplo muestra cómo se define una regla con un sufijo específico (.html) y cómo configurar redirecciones:
'urlManager' => [
    // ... configuración básica ...
    'enableStrictParsing' => true, // Solo permite las URLs definidas aquí [3]
    'suffix' => '.html', // Agrega el sufijo .html a todas las URLs (global) [3]
    'rules' => [
        // Regla que sobreescribe el sufijo global, o lo aplica [3]
        [
            'pattern' => 'post/<slug:[-a-zA-Z0-9]+>',
            'route' => 'post/view',
            'suffix' => '.html',
        ],
        // Redirección de URLs antiguas
        'antigua-url' => 'site/redirect',
        
        // Regla para páginas estáticas
        'pagina/<view:[a-zA-Z]+>' => 'site/page',
    ],
],
4. Reglas RESTful (APIs y Verbos HTTP)
Estas reglas permiten que una misma URL (api/posts) tenga diferentes comportamientos dependiendo del método (verbo HTTP) usado (GET, POST, PUT, DELETE):
'rules' => [
    // Usa la clase UrlRule para generar reglas REST automáticamente para el controlador 'api/user' [4]
    [
        'class' => 'yii\rest\UrlRule',
        'controller' => 'api/user',
    ],
    
    // Reglas explícitas para APIs RESTful
    'GET api/posts' => 'post/list',
    'POST api/posts' => 'post/create',
    'PUT api/posts/<id:\d+>' => 'post/update',
    'DELETE api/posts/<id:\d+>' => 'post/delete',
],
5. Uso del Código en Vistas y Controladores
Una vez que las reglas están definidas en web.php, Yii2 utiliza estas reglas para generar las URLs correctas dentro de la aplicación:
En Vistas (Generando enlaces):
<?= Html::a('Ver post', ['post/view', 'id' => $post->id, 'slug' => $post->slug]) ?>
• Resultado: Si existe la regla 'post/<id:\d+>' => 'post/view', esto generaría una URL como /post/123.
En Controladores (Redirecciones):
return $this->redirect(['post/view', 'id' => $id]);
• Resultado: Esto redirige al usuario a la URL amigable correspondiente a la acción post/view con el ID especificado.
.wcgs_xzoom-preview {
    background: #ffffff !important;
}
<!-- 

Online HTML, CSS and JavaScript editor to run code online.

-->

<!DOCTYPE html>

<html lang="en">

​

<head>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

  <link rel="stylesheet" href="style.css" />

  <title>Browser</title>

</head>

​

<body>

  <h1>

    Write, edit and run HTML, CSS and JavaScript code online.

  </h1>

  <p>

    Our HTML editor updates the webview automatically in real-time as you write code.

  </p>

  <script src="script.js"></script>

</body>

​

</html>
Proc Compare Base=project.potential_cohort Compare=project.OLD_potential_cohort;
   ID PatID;
   Var PatID NVAF;
run;
string button.generate_Sales_offer1(String recordId)
{
// ========== Inputs / Variables ==========
moduleAPI = "Sales_Offers";
// Your module API name
//recordId = "5971686000117622076";
templateName = "Sales Offer";
// Name of the mail merge template defined in CRM
desiredFileName = "Generated Sales Offer";
outputFormat = "pdf";
// ========== Build Request Body ==========
mergeEntry = Map();
tMap = Map();
tMap.put("name",templateName);
mergeEntry.put("mail_merge_template",tMap);
mergeEntry.put("file_name",desiredFileName);
mergeEntry.put("output_format",outputFormat);
wrapper = Map();
mergeList = List();
mergeList.add(mergeEntry);
wrapper.put("download_mail_merge",mergeList);
// ========== Call Download Mail Merge API ==========
download_resp = invokeurl
[
	url :"https://www.zohoapis.com/crm/v8/" + moduleAPI + "/" + recordId + "/actions/download_mail_merge"
	type :POST
	body:wrapper.toString()
	connection:"mail_merge"
];
info download_resp;
download_resp.setParamName("file");
response = invokeurl
[
	url :"https://www.zohoapis.com/crm/v8/files"
	type :POST
	files:download_resp
	connection:"newzohocrm"
];
info response;
if(response.get("data") != null && response.get("data").size() > 0)
{
	fileId = response.get("data").get(0).get("details").get("id");
	// Prepare file upload field value
	fileMap = Map();
	fileMap.put("file_id",fileId);
	fileList = List();
	fileList.add(fileMap);
}
//
///////////////
// ========= Upload file to CRM Attachments ========= //
bodyMap = Map();
attachList = List();
attachList.add({"id":fileId});
bodyMap.put("attachments",attachList);
attachUrl = "https://www.zohoapis.com/crm/v8/" + moduleAPI + "/" + recordId + "/Attachments";
attachResp = invokeurl
[
	url :attachUrl
	type :POST
	files:download_resp
	connection:"newzohocrm"
];
info attachResp;
if(attachResp.get("data").size() > 0)
{
	return "Sales Offer has been generated and attached successfully.";
}
else
{
	return "There was an error while generating or attaching the Sales Offer. Please try again.";
}
//return "";
}
string standalone.test_Ali_Docu_Sign()
{
try 
{
	// ================= FETCH DEAL & VALIDATE ==================
	Deal_id = 5971686000063835007;
	get_Details = invokeurl
	[
		url :"https://www.zohoapis.com/crm/v8/Deals/" + Deal_id
		type :GET
		connection:"newzohocrm"
	];
	data = get_Details.get("data");
	for each  rec in data
	{
		existing_envelope_id = rec.get("Envelope_ID");
		if(existing_envelope_id != null && existing_envelope_id != "")
		{
			return "Document already sent for this reservation";
		}
		unit_map = rec.get("Unit");
		unit_name = "";
		if(unit_map != null)
		{
			unit_name = ifnull(unit_map.get("name"),"");
		}
		//info unit_name;
		customer_id = rec.get("Contact_Name").get("id");
		contact_Details = zoho.crm.getRecordById("Contacts",customer_id);
		single_buyer_email = ifnull(contact_Details.get("Email"),"");
		single_buyer_name = ifnull(contact_Details.get("Full_Name"),"");
		refined_countryandcode = standalone.Refine_phone_number_and_country_code(customer_id);
		info refined_countryandcode;
		single_buyer_phone = ifnull(contact_Details.get("Mobile"),"");
		single_buyer_country_code = ifnull(contact_Details.get("Country_Code"),"");
		// use Mobile for OTP
		Joint_buyer_details = ifnull(rec.get("Joint_buyer_name"),null);
		if(Joint_buyer_details != null)
		{
			Joint_buyer_id = Joint_buyer_details.get("id");
			Joint_Buyer_contact_details = zoho.crm.getRecordById("Contacts",Joint_buyer_id);
			Joint_Buyer_email = ifnull(Joint_Buyer_contact_details.get("Email"),"");
			Joint_Buyer_name = ifnull(Joint_Buyer_contact_details.get("Full_Name"),"");
			Joint_Buyer_Mobile = ifnull(Joint_Buyer_contact_details.get("Mobile"),"");
			Joint_Buyer_country_code = ifnull(Joint_Buyer_contact_details.get("Country_Code"),"");
			info "Join Buyer Mobile: " + Joint_Buyer_Mobile;
		}
		Reservation_File_Upload = rec.get("Reservation_File_Upload");
		for each  file_data in Reservation_File_Upload
		{
			file_id = file_data.get("File_Id__s");
			response = invokeurl
			[
				url :"https://www.zohoapis.com/crm/v8/files?id=" + file_id
				type :GET
				connection:"newzohocrm"
			];
		}
	}
	// ================= CONVERT TO BASE64 ==================
	base64_pdf = zoho.encryption.base64Encode(response);
	doc = Map();
	doc.put("documentBase64",base64_pdf);
	doc.put("name","Sale Purchase Agreement");
	doc.put("fileExtension","docx");
	doc.put("documentId","1");
	// ================= SIGNERS ==================
	sign_here_buyer = List();
	sign_here_buyer.add({"anchorString":"Signed by Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"168","anchorYOffset":"32","required":true});
	sign_here_buyer.add({"anchorString":"Signed for and on behalf of the Purchaser","anchorUnits":"pixels","anchorXOffset":"172","anchorYOffset":"4","required":true});
	date_signed_buyer = List();
	date_signed_buyer.add({"anchorString":"IN WITNESS WHEREOF the Parties have executed this Agreement.","anchorUnits":"pixels","anchorXOffset":"175","anchorYOffset":"115","required":false});
	date_signed_buyer.add({"anchorString":"I/We acknowledge that in the event there is a Constitution, I/we agree to comply with the provisions","anchorUnits":"pixels","anchorXOffset":"155","anchorYOffset":"215","required":false});
	date_signed_buyer.add({"anchorString":"Signed for and on behalf of the Purchaser","anchorUnits":"pixels","anchorXOffset":"167","anchorYOffset":"63","required":false});
	initials_buyer = List();
	initials_buyer.add({"anchorString":"Purchaser’s initials","anchorUnits":"pixels","anchorXOffset":"120","anchorYOffset":"16","required":true});
	tabs_buyer = Map();
	tabs_buyer.put("signHereTabs",sign_here_buyer);
	tabs_buyer.put("dateSignedTabs",date_signed_buyer);
	tabs_buyer.put("initialHereTabs",initials_buyer);
	signer1 = Map();
	signer1.put("email",single_buyer_email);
	signer1.put("name",single_buyer_name);
	signer1.put("recipientId","1");
	signer1.put("routingOrder","2");
	signer1.put("tabs",tabs_buyer);
	// ---- Phone OTP Identity Verification ----
	if(single_buyer_phone != "")
	{
		identity_verification = Map();
		identity_verification.put("workflowId","c368e411-1592-4001-a3df-dca94ac539ae");
		input_option = Map();
		input_option.put("name","phone_number_list");
		input_option.put("valueType","PhoneNumberList");
		phone_list = List();
		phone_item = Map();
		phone_item.put("countryCode",single_buyer_country_code);
		phone_item.put("number",single_buyer_phone);
		phone_list.add(phone_item);
		input_option.put("phoneNumberList",phone_list);
		identity_verification.put("inputOptions",{input_option});
		signer1.put("identityVerification",identity_verification);
	}
	signers_list = List();
	signers_list.add(signer1);
	// ---- Witness for Signer 1 ----
	sign_here_witness = List();
	sign_here_witness.add({"anchorString":"IN WITNESS WHEREOF the Parties have executed this Agreement","anchorUnits":"pixels","anchorXOffset":"168","anchorYOffset":"168","required":true});
	sign_here_witness.add({"anchorString":"Signed for and on behalf of the Purchaser","anchorUnits":"pixels","anchorXOffset":"172","anchorYOffset":"90","required":true});
	text_tabs_witness = List();
	text_tabs_witness.add({"anchorString":"IN WITNESS WHEREOF the Parties have executed this Agreement","anchorUnits":"pixels","anchorXOffset":"160","anchorYOffset":"190","tabLabel":"witness1_name","value":"","locked":false,"required":true});
	text_tabs_witness.add({"anchorString":"IN WITNESS WHEREOF the Parties have executed this Agreement","anchorUnits":"pixels","anchorXOffset":"160","anchorYOffset":"218","tabLabel":"witness1_address","value":"","locked":false,"required":true});
	text_tabs_witness.add({"anchorString":"IN WITNESS WHEREOF the Parties have executed this Agreement","anchorUnits":"pixels","anchorXOffset":"160","anchorYOffset":"243","tabLabel":"witness1_occupation","value":"","locked":false,"required":true});
	text_tabs_witness.add({"anchorString":"Signed for and on behalf of the Purchaser","anchorUnits":"pixels","anchorXOffset":"160","anchorYOffset":"110","tabLabel":"witness2_name","value":"","locked":false,"required":true});
	text_tabs_witness.add({"anchorString":"Signed for and on behalf of the Purchaser","anchorUnits":"pixels","anchorXOffset":"160","anchorYOffset":"135","tabLabel":"witness2_address","value":"","locked":false,"required":true});
	tabs_witness = Map();
	tabs_witness.put("signHereTabs",sign_here_witness);
	tabs_witness.put("textTabs",text_tabs_witness);
	witness1 = Map();
	witness1.put("witnessFor","1");
	witness1.put("recipientId","2");
	witness1.put("routingOrder","3");
	witness1.put("email","");
	witness1.put("name","");
	witness1.put("tabs",tabs_witness);
	// ---- Signer 2 (Joint Buyer) ----
	if(Joint_Buyer_Mobile != "")
	{
		joint_identity_verification = Map();
		joint_identity_verification.put("workflowId","c368e411-1592-4001-a3df-dca94ac539ae");
		joint_input_option = Map();
		joint_input_option.put("name","phone_number_list");
		joint_input_option.put("valueType","PhoneNumberList");
		joint_phone_list = List();
		joint_phone_item = Map();
		joint_phone_item.put("countryCode",Joint_Buyer_country_code);
		joint_phone_item.put("number",Joint_Buyer_Mobile);
		joint_phone_list.add(joint_phone_item);
		joint_input_option.put("phoneNumberList",joint_phone_list);
		joint_identity_verification.put("inputOptions",{joint_input_option});
	}
	if(Joint_buyer_details != null)
	{
		sign_here_joint = List();
		sign_here_joint.add({"anchorString":"Signed by Joint Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"172","anchorYOffset":"28","required":true});
		sign_here_joint.add({"anchorString":"Signed by joint Purchaser","anchorUnits":"pixels","anchorXOffset":"172","anchorYOffset":"29","required":true});
		sign_here_joint.add({"anchorString":"Signed for and on behalf of the Joint Purchaser","anchorUnits":"pixels","anchorXOffset":"175","anchorYOffset":"5","required":true});
		date_signed_joint = List();
		date_signed_joint.add({"anchorString":"Signed by Joint Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"173","anchorYOffset":"79","required":false});
		date_signed_joint.add({"anchorString":"Signed by joint Purchaser","anchorUnits":"pixels","anchorXOffset":"182","anchorYOffset":"80","required":false});
		date_signed_joint.add({"anchorString":"Signed for and on behalf of the Joint Purchaser","anchorUnits":"pixels","anchorXOffset":"174","anchorYOffset":"65","required":false});
		/////////
		/////////////////
		initials_buyer2 = List();
		initials_buyer2.add({"anchorString":"Purchaser’s initials","anchorUnits":"pixels","anchorXOffset":"200","anchorYOffset":"16","required":true});
		tabs_joint = Map();
		tabs_joint.put("signHereTabs",sign_here_joint);
		tabs_joint.put("dateSignedTabs",date_signed_joint);
		tabs_joint.put("initialHereTabs",initials_buyer2);
		signer2 = Map();
		signer2.put("email",Joint_Buyer_email);
		signer2.put("name",Joint_Buyer_name);
		signer2.put("identityVerification",joint_identity_verification);
		signer2.put("recipientId","3");
		signer2.put("routingOrder","4");
		signer2.put("tabs",tabs_joint);
		signers_list.add(signer2);
		// ---- Witness for Signer 2 (Joint Buyer) ----
		sign_here_witness2 = List();
		sign_here_witness2.add({"anchorString":"Signed by Joint Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"173","anchorYOffset":"140","required":true});
		//////Changed
		sign_here_witness2.add({"anchorString":"Signed for and on behalf of the Joint Purchaser","anchorUnits":"pixels","anchorXOffset":"174","anchorYOffset":"103","required":true});
		//////Changed
		//////////////////////
		/////////////////////////////
		text_tabs_witness2 = List();
		text_tabs_witness2.add({"anchorString":"Signed by Joint Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"173","anchorYOffset":"160","tabLabel":"witness3_name","value":"","locked":false,"required":true});
		//////Changed
		text_tabs_witness2.add({"anchorString":"Signed by Joint Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"173","anchorYOffset":"185","tabLabel":"witness3_address","value":"","locked":false,"required":true});
		///changed
		text_tabs_witness2.add({"anchorString":"Signed by Joint Individual Purchaser","anchorUnits":"pixels","anchorXOffset":"173","anchorYOffset":"210","tabLabel":"witness3_occupation","value":"","locked":false,"required":true});
		///changed
		////////////
		///////////////////////////
		text_tabs_witness2.add({"anchorString":"Signed for and on behalf of the Joint Purchaser","anchorUnits":"pixels","anchorXOffset":"174","anchorYOffset":"118","tabLabel":"witness4_name","value":"","locked":false,"required":true});
		///chnages
		text_tabs_witness2.add({"anchorString":"Signed for and on behalf of the Joint Purchaser","anchorUnits":"pixels","anchorXOffset":"170","anchorYOffset":"145","tabLabel":"witness4_address","value":"","locked":false,"required":true});
		///changed
		witness2_tabs = Map();
		witness2_tabs.put("signHereTabs",sign_here_witness2);
		witness2_tabs.put("textTabs",text_tabs_witness2);
		witness2 = Map();
		witness2.put("witnessFor","3");
		witness2.put("recipientId","4");
		witness2.put("routingOrder","5");
		witness2.put("email","");
		witness2.put("name","");
		witness2.put("tabs",witness2_tabs);
	}
	// ---- Manager Final Signer (Lani Alipio) ----
	sign_here_manager = List();
	sign_here_manager.add({"documentId":"1","pageNumber":"2","xPosition":"100","yPosition":"600"});
	tabs_manager = Map();
	tabs_manager.put("signHereTabs",sign_here_manager);
	manager_signer = Map();
	manager_signer.put("email","muhammad.kaleem@leosops.com");
	///
	manager_signer.put("name","Lani Alipio");
	manager_signer.put("recipientId","11");
	if(Joint_buyer_details != null)
	{
		manager_signer.put("routingOrder","6");
	}
	else
	{
		manager_signer.put("routingOrder","5");
	}
	manager_signer.put("tabs",tabs_manager);
	signers_list.add(manager_signer);
	// ---- CC LIST (Mark + Lani) ----
	cc_list = List();
	cc_mark = Map();
	cc_mark.put("email","m.awais@leosuk.com");
	//
	cc_mark.put("name","Mark");
	cc_mark.put("recipientId","12");
	cc_mark.put("routingOrder","1");
	cc_list.add(cc_mark);
	cc_noor = Map();
	cc_noor.put("email","muhammad.usman@leosops.com");
	///
	cc_noor.put("name","Lani Alipio");
	cc_noor.put("recipientId","13");
	cc_noor.put("routingOrder","1");
	cc_list.add(cc_noor);
	/////////////
	//////
	cc_salesadmin = Map();
	cc_salesadmin.put("email","syed.ali@leosuk.com");
	cc_salesadmin.put("name","Syed");
	cc_salesadmin.put("recipientId","15");
	cc_salesadmin.put("routingOrder","1");
	cc_list.add(cc_salesadmin);
	///////////
	//////////////
	//////////////////
	// 	cc_awais = Map();
	// 	cc_awais.put("email","m.awais@leosuk.com");
	// 	cc_awais.put("name","Awais khan");
	// 	cc_awais.put("recipientId","16");
	// 	cc_awais.put("routingOrder","1");
	// 	cc_list.add(cc_awais);
	////
	////
	// 	cc_ana = Map();
	// 	cc_ana.put("email","anamay@leosuk.com");
	// 	//////
	// 	cc_ana.put("name","Ana May S. Zamora");
	// 	cc_ana.put("recipientId","14");
	// 	cc_ana.put("routingOrder","1");
	// 	cc_list.add(cc_ana);
	// ================= RECIPIENTS MAP ==================
	recipients = Map();
	recipients.put("signers",signers_list);
	if(Joint_buyer_details != null)
	{
		recipients.put("witnesses",{witness1,witness2});
	}
	else
	{
		recipients.put("witnesses",{witness1});
	}
	recipients.put("carbonCopies",cc_list);
	envelope = Map();
	envelope.put("documents",{doc});
	envelope.put("emailSubject","SPA for Signature - " + unit_name + " Final");
	envelope.put("emailBlurb","Greetings!<br><br>" + "I hope you are doing well.<br><br>" + "Please your are requested to select the \"Draw\" option when signing the Sales and Purchase Agreement (SPA) via DocuSign, this is an important step to ensure compliance with the Dubai Land Department (DLD) requirements. The DLD mandates that electronic signatures on official documents, such as the SPA, must match the signature in the individual's passport. Using the \"Draw\" option in DocuSign allows you to replicate your handwritten signature, which is essential for this compliance.<br><br>" + "Steps to Draw Your Signature in DocuSign:<br><br>" + "Open the Document: Click on the link provided in the DocuSign email to access the SPA.<br><br>" + "Select the Signature Field: Click on the designated area where your signature is required.<br><br>" + "Choose the \"Draw\" Option: When prompted to choose a signature style, select the \"Draw\" option.<br><br>" + "Draw Your Signature: Use your mouse, stylus, or finger (on a touchscreen device) to draw your signature in the provided space.<br><br>" + "Confirm the Signature: Once satisfied with your drawn signature, confirm it to apply it to the document.<br><br>" + "This method ensures that your electronic signature closely matches your passport signature, meeting the DLD's requirements.<br><br>" + "Regarding the Selection of a Witness:<br><br>" + "We appreciate your involvement in selecting a witness for the SPA. Please provide the name and contact details of your chosen witness at your earliest convenience. Your witness will receive an invitation to sign the SPA via DocuSign once their details are confirmed.<br><br>" + "Next Steps:<br><br>" + "Sign the SPA: Please sign the SPA using the \"Draw\" option as outlined above.<br><br>" + "Select a Witness: Provide the name, Address and Occupation of your chosen witness.<br><br>" + "Witness Signs: Once your witness is added, they will receive an invitation to sign the SPA.<br><br>" + "If you have any questions or need further assistance during this process, please don't hesitate to reach out. We're here to help and ensure a smooth and compliant signing experience.<br><br>" + "Welcome once again to the Leo's Development family!");
	envelope.put("status","sent");
	envelope.put("recipients",recipients);
	// ================= FETCH DOCUSIGN ACCESS TOKEN ==================
	access_token_response = invokeurl
	[
		url :"https://www.zohoapis.com/crm/v6/settings/variables/5971686000102746225"
		type :GET
		connection:"newzohocrm"
	];
	access_token = access_token_response.get("variables").get(0).get("value");
	headers = Map();
	headers.put("Authorization","Bearer " + access_token);
	headers.put("Content-Type","application/json");
	response = invokeurl
	[
		url :"https://eu.docusign.net/restapi/v2.1/accounts/2a0daa7d-a770-4979-8208-9543d21f12e5/envelopes"
		type :POST
		parameters:envelope.toString()
		headers:headers
	];
	info response;
	if(response.get("status") == "sent")
	{
		envelopeId = response.get("envelopeId");
		update_map = Map();
		update_map.put("Envelope_ID",envelopeId);
		update_map.put("Next_Status_Sync_Time",zoho.currenttime.toString("yyyy-MM-dd'T'HH:mm:ss","Asia/Dubai"));
		update_map.put("Docu_Sign_Status","Sent");
		Update_Rec = zoho.crm.updateRecord("Deals",Deal_id,update_map);
		return "Document has been successfully sent to all recipients.";
	}
	else
	{
		return "Failed to send the document. Please verify that the customer details, including phone number and country code, are correct.";
	}
}
catch (e)
{
	sendmail
	[
		from :zoho.loginuserid
		to :"zoho.failure@leosinternational.zohodesk.com"
		subject :"[DocuSign Error | Deal " + Deal_id + "]"
		message :"An error occurred while sending the document via DocuSign.\n\nDeal ID: " + Deal_id + "\nError Details: " + e.toString()
	]
	return "Error occurred: " + e.toString();
}
return "";
}
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: What's on in Melbourne this week! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n Hey Melbourne, happy Monday and hope you all had a Fab weekend! Please see below for what's on this week. "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Xero Café :coffee:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n :new-thing: *This week we are offering:* \n\n :caramel-slice: *Sweet Treats*: Selection of biscuits & Lamingtons\n\n :coffee: Caramel Cappuccino"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": " Wednesday, 8th October :calendar-date-8:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " \n\n:flag-fr: *Lunch*: Join us at 12.00pm as we head off to France for Lunch. Menu is in the :thread: "
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Thursday, 2nd October :calendar-date-2:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " :croissant: Join us for some yummy breakfast featuring a French special from *8.30am - 10.30am* in the *Wominjeka Breakout Space*. Menu is in the :thread:  "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " What else :heart: \n Stay tuned to this channel, and make sure you're subscribed to the <https://calendar.google.com/calendar/u/0?cid=Y19xczkyMjk5ZGlsODJzMjA4aGt1b3RnM2t1MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Melbourne Social Calendar*> :party-wx:"
			}
		}
	]
}
# Descarga e instala nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

# en lugar de reiniciar la shell
\. "$HOME/.nvm/nvm.sh"

# Descarga e instala Node.js:
nvm install 22

# Verify the Node.js version:
node -v # Should print "v22.20.0".

# Verifica versión de npm:
npm -v # Debería mostrar "10.9.3".
Unfortunately our team is not trained to support websites built with Next.js which is a custom javascript framework.  so we cannot complete integrations on web apps like this, they have to be client integrations.  You can provide the help docs to guide them.
1. Instalar Apache2 (si no está instalado)
bash
# Ubuntu/Debian
sudo apt update
sudo apt install apache2

# CentOS/RHEL/Fedora
sudo dnf install httpd
2. Estructura de archivos importantes
Configuración principal: /etc/apache2/apache2.conf

Sites disponibles: /etc/apache2/sites-available/

Sites activos: /etc/apache2/sites-enabled/

Document root por defecto: /var/www/html/

3. Crear un Virtual Host
Paso 1: Crear el directorio del sitio
bash
sudo mkdir -p /var/www/misitio.local/public_html
sudo mkdir -p /var/www/misitio.local/logs
Paso 2: Crear archivo de prueba
bash
sudo nano /var/www/misitio.local/public_html/index.html
Contenido del archivo:

html
<!DOCTYPE html>
<html>
<head>
    <title>Mi Sitio Local</title>
</head>
<body>
    <h1>¡Bienvenido a misitio.local!</h1>
    <p>Este es un virtual host de prueba.</p>
</body>
</html>
Paso 3: Configurar permisos
bash
sudo chown -R $USER:$USER /var/www/misitio.local/public_html
sudo chmod -R 755 /var/www
4. Crear archivo de Virtual Host
bash
sudo nano /etc/apache2/sites-available/misitio.local.conf
Contenido del archivo:

apache
<VirtualHost *:80>
    # Dirección de email del administrador (opcional)
    ServerAdmin webmaster@misitio.local
    
    # Directorio raíz del sitio
    DocumentRoot /var/www/misitio.local/public_html
    
    # Dominio del sitio
    ServerName misitio.local
    ServerAlias www.misitio.local
    
    # Archivos de log
    ErrorLog /var/www/misitio.local/logs/error.log
    CustomLog /var/www/misitio.local/logs/access.log combined
    
    # Configuraciones adicionales
    <Directory /var/www/misitio.local/public_html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
5. Activar el Virtual Host
bash
# Activar el sitio
sudo a2ensite misitio.local.conf

# Desactivar sitio por defecto (opcional)
sudo a2dissite 000-default.conf

# Recargar Apache
sudo systemctl reload apache2
6. Configurar el archivo hosts
bash
sudo nano /etc/hosts
Agregar esta línea:

text
127.0.0.1   misitio.local www.misitio.local
7. Verificar la configuración
bash
# Verificar sintaxis de Apache
sudo apache2ctl configtest

# Reiniciar Apache
sudo systemctl restart apache2
8. Probar el sitio
Abre tu navegador y visita: http://misitio.local

Configuración adicional útil
Para HTTPS (opcional)
bash
# Habilitar módulo SSL
sudo a2enmod ssl

# Crear certificado auto-firmado
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/ssl/private/misitio.local.key \
    -out /etc/ssl/certs/misitio.local.crt
Virtual Host con HTTPS
apache
<VirtualHost *:443>
    ServerName misitio.local
    DocumentRoot /var/www/misitio.local/public_html
    
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/misitio.local.crt
    SSLCertificateKeyFile /etc/ssl/private/misitio.local.key
</VirtualHost>
Comandos útiles
bash
# Ver sitios activos
sudo apache2ctl -S

# Ver módulos activos
sudo apache2ctl -M

# Reiniciar Apache
sudo systemctl restart apache2

# Ver estado de Apache
sudo systemctl status apache2
¡Listo! Ahora tienes un dominio local funcionando con Apache2. Puedes repetir estos pasos para crear tantos virtual hosts como necesites.

para laravel

Te explico cómo configurar un Virtual Host para Laravel en Apache2:

1. Estructura de directorios de Laravel
En Laravel, el document root debe apuntar a la carpeta public:

text
/var/www/tu-proyecto-laravel/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/     ← Esta es la carpeta raíz para Apache
├── resources/
├── routes/
├── storage/
└── vendor/
2. Crear el Virtual Host para Laravel
bash
sudo nano /etc/apache2/sites-available/laravel.local.conf
Contenido del archivo:

apache
<VirtualHost *:80>
    ServerName laravel.local
    ServerAlias www.laravel.local
    DocumentRoot /var/www/tu-proyecto-laravel/public
    ServerAdmin admin@laravel.local

    <Directory /var/www/tu-proyecto-laravel/public>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
        FallbackResource /index.php
    </Directory>

    # Configuración específica para Laravel
    <Directory /var/www/tu-proyecto-laravel>
        Options FollowSymLinks
        AllowOverride None
        Require all denied
    </Directory>

    # Logs
    ErrorLog /var/log/apache2/laravel_error.log
    CustomLog /var/log/apache2/laravel_access.log combined

    # Headers para mejor seguridad y performance
    Header always set X-Content-Type-Options nosniff
    Header always set X-Frame-Options DENY
    Header always set X-XSS-Protection "1; mode=block"
</VirtualHost>
3. Configuración alternativa más específica para Laravel
apache
<VirtualHost *:80>
    ServerName laravel.local
    DocumentRoot /var/www/tu-proyecto-laravel/public

    <Directory /var/www/tu-proyecto-laravel/public>
        # Habilitar rewrite module
        RewriteEngine On

        # Redirect Trailing Slashes If Not A Folder...
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_URI} (.+)/$
        RewriteRule ^ %1 [L,R=301]

        # Handle Front Controller...
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.php [L]

        # Configuraciones de seguridad y permisos
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Configuración para carpeta storage (importante para Laravel)
    Alias /storage /var/www/tu-proyecto-laravel/storage/app/public
    <Directory /var/www/tu-proyecto-laravel/storage/app/public>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/laravel_error.log
    CustomLog ${APACHE_LOG_DIR}/laravel_access.log combined
</VirtualHost>
4. Configurar permisos de Laravel
bash
# Navegar al directorio del proyecto
cd /var/www/tu-proyecto-laravel

# Permisos para storage y cache
sudo chown -R $USER:www-data /var/www/tu-proyecto-laravel
sudo find /var/www/tu-proyecto-laravel -type f -exec chmod 664 {} \;
sudo find /var/www/tu-proyecto-laravel -type d -exec chmod 775 {} \;

# Permisos específicos para storage y bootstrap/cache
sudo chgrp -R www-data storage bootstrap/cache
sudo chmod -R ug+rwx storage bootstrap/cache

# Permisos para el usuario www-data (Apache)
sudo chown -R www-data:www-data storage bootstrap/cache
5. Activar el sitio y módulos necesarios
bash
# Activar módulos necesarios
sudo a2enmod rewrite
sudo a2enmod headers

# Activar el sitio de Laravel
sudo a2ensite laravel.local.conf

# Desactivar sitio por defecto (opcional)
sudo a2dissite 000-default.conf

# Recargar configuración
sudo systemctl reload apache2
6. Configurar el archivo hosts
bash
sudo nano /etc/hosts
Agregar:

text
127.0.0.1    laravel.local
7. Configurar .env y aplicación Laravel
bash
# Copiar archivo de entorno
cp .env.example .env

# Generar key de la aplicación
php artisan key:generate

# Configurar cache y rutas
php artisan config:cache
php artisan route:cache
8. Virtual Host para entorno de producción
apache
<VirtualHost *:80>
    ServerName tudominio.com
    ServerAlias www.tudominio.com
    DocumentRoot /var/www/tu-proyecto-laravel/public

    <Directory /var/www/tu-proyecto-laravel/public>
        AllowOverride All
        Options +FollowSymLinks -Indexes
        Require all granted

        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^ index.php [L]
    </Directory>

    # Configuración de logs
    ErrorLog /var/log/apache2/laravel_error.log
    CustomLog /var/log/apache2/laravel_access.log combined

    # Configuración de performance
    EnableSendfile on
    Timeout 300
</VirtualHost>
9. Comandos de verificación
bash
# Verificar sintaxis de Apache
sudo apache2ctl configtest

# Ver permisos
ls -la /var/www/tu-proyecto-laravel/storage/
ls -la /var/www/tu-proyecto-laravel/bootstrap/cache/

# Ver logs en tiempo real
sudo tail -f /var/log/apache2/laravel_error.log
10. Solución de problemas comunes
Error: "No input file specified"
apache
# En el Virtual Host, agregar:
<IfModule mod_php7.c>
    php_admin_value open_basedir "/var/www/tu-proyecto-laravel/:/tmp/:/proc/"
</IfModule>
Error de permisos:
bash
sudo chcon -R -t httpd_sys_rw_content_t /var/www/tu-proyecto-laravel/storage
sudo chcon -R -t httpd_sys_rw_content_t /var/www/tu-proyecto-laravel/bootstrap/cache
Para habilitar mod_rewrite:
bash
sudo a2enmod rewrite
sudo systemctl restart apache2
¡Con esta configuración tu aplicación Laravel debería funcionar correctamente! Recuerda que el punto más importante es que el DocumentRoot apunte a la carpeta public de tu proyecto Laravel.

name: cool_et
services:
  hoarder-chrome:
    cpu_shares: 90
    command: []
    deploy:
      resources:
        limits:
          memory: 1024M
    image: seleniarm/standalone-chromium:latest
    labels:
      icon: https://icon.casaos.io/main/all/hoarder-web.png
    platform: linux/arm64
    restart: unless-stopped
    ports: []
    volumes: []
    devices: []
    cap_add: []
    environment: []
    network_mode: bridge
    privileged: false
    container_name: ""
    hostname: ""
  hoarder-meilisearch:
    cpu_shares: 90
    command: []
    deploy:
      resources:
        limits:
          memory: 3797M
    environment:
      - HOARDER_VERSION=latest
      - MEILI_MASTER_KEY=aqxYm5UWCphGhkAu5fBcSJAPbSzeoiC2X
      - MEILI_NO_ANALYTICS=true
      - NEXTAUTH_SECRET=qxo0NvLCphGhkAu5fBcS1B0MxCeoig8G
      - NEXTAUTH_URL=http://localhost:3088
    image: getmeili/meilisearch:v1.6
    labels:
      icon: https://icon.casaos.io/main/all/hoarder-web.png
    platform: linux/arm64
    restart: unless-stopped
    volumes:
      - type: bind
        source: /DATA/AppData/karakeep/meili
        target: /meili_data
    ports: []
    devices: []
    cap_add: []
    network_mode: bridge
    privileged: false
    container_name: ""
    hostname: ""
  web:
    cpu_shares: 90
    command: []
    deploy:
      resources:
        limits:
          memory: 2048M
    environment:
      - BROWSER_WEB_URL=http://hoarder-chrome:9222
      - DATA_DIR=/data
      - HOARDER_VERSION=latest
      - MEILI_ADDR=http://hoarder-meilisearch:7700
      - MEILI_MASTER_KEY=aqxYm5UWCphGhkAu5fBcSJAPbSzeoiC2X
      - NEXTAUTH_SECRET=qxo0NvLCphGhkAu5fBcS1B0MxCeoig8G
      - NEXTAUTH_URL=http://localhost:3088
      - OPENAI_API_KEY=
    image: ghcr.io/karakeep-app/karakeep:latest
    labels:
      icon: https://icon.casaos.io/main/all/hoarder-web.png
    platform: linux/arm64
    ports:
      - target: 3000
        published: "3088"
        protocol: tcp
    restart: unless-stopped
    volumes:
      - type: bind
        source: /DATA/Documents/Hoarder
        target: /data
    devices: []
    cap_add: []
    network_mode: bridge
    privileged: false
    container_name: ""
    hostname: ""
x-casaos:
  author: self
  category: self
  hostname: 10.0.0.6
  icon: https://icon.casaos.io/main/all/hoarder-web.png
  index: /
  is_uncontrolled: false
  port_map: "3088"
  scheme: http
  store_app_id: cool_et
  title:
    custom: Karakeep
add_filter(
    'wpcp_max_input_bar_value',
    function ( $value ) {
        return 4000;
    }
);
como lo integro con vscode
Para integrar Pieces for Developers con Visual Studio Code, debes instalar la extensión oficial de Pieces desde el marketplace de VS Code. Con esto, podrás acceder a tu Pieces Drive y a las funcionalidades del Copilot de Pieces directamente desde tu editor. 
Requisitos previos
Asegúrate de que la aplicación de escritorio de Pieces y PiecesOS estén instalados y en ejecución. En Linux, el servicio de PiecesOS debe estar funcionando en segundo plano. 
Pasos para integrar Pieces con VS Code
1. Abre el gestor de extensiones de VS Code
Inicia Visual Studio Code.
Haz clic en el ícono de extensiones en la barra de actividades de la izquierda (o usa el atajo Ctrl+Shift+X). 
2. Busca la extensión de Pieces
En el campo de búsqueda del gestor de extensiones, escribe Pieces for VS Code.
La extensión oficial de Pieces debería aparecer en los resultados de la búsqueda. 
3. Instala la extensión
Haz clic en el botón Install en la extensión Pieces for VS Code.
Después de la instalación, es posible que se te pida reiniciar VS Code para que los cambios surtan efecto. 
4. Empieza a usar Pieces en VS Code
Una vez que la extensión esté instalada y PiecesOS esté en funcionamiento, verás las opciones de Pieces dentro de tu entorno de VS Code. 
Guardar fragmentos de código: Selecciona un bloque de código, haz clic derecho y elige Pieces > Save Selection to Pieces. Pieces guardará el fragmento en tu Drive y le añadirá metadatos generados por IA para que sea fácil de encontrar más tarde.
Buscar fragmentos: Accede a la búsqueda de Pieces desde la paleta de comandos (Ctrl+Shift+P y busca Pieces: Search Pieces) o desde el menú contextual. Podrás buscar en tu repositorio personal de materiales.
Usar el Copilot de Pieces: Al igual que la búsqueda, puedes interactuar con el Copilot de Pieces. Para acciones contextuales, selecciona un fragmento de código, haz clic derecho y elige una de las opciones del menú de Pieces, como Explain Selection with Copilot o Comment Selection with Copilot.
Paso 1: Habilitar Snap en Linux Mint
Abre la terminal.
Ejecuta los siguientes comandos para instalar snapd y asegurarte de que los paquetes de Snap funcionan correctamente en tu sistema:
sh
sudo rm /etc/apt/preferences.d/nosnap.pref
sudo apt update
sudo apt install snapd
Usa el código con precaución.

Reinicia tu sistema o sal y vuelve a iniciar sesión para asegurarte de que la configuración de Snap se haya aplicado correctamente. 
Paso 2: Instalar PiecesOS y Pieces for Developers
La aplicación Pieces tiene dos componentes principales:
PiecesOS: El servicio en segundo plano que gestiona la IA y la lógica de la aplicación.
Pieces for Developers: La aplicación de escritorio que interactúa con PiecesOS. 
Sigue estos pasos en la terminal para instalarlos:
Instala PiecesOS:
sh
sudo snap install pieces-os
Usa el código con precaución.

Habilita el control de procesos:
sh
sudo snap connect pieces-os:process-control :process-control
Usa el código con precaución.

Instala la aplicación de escritorio Pieces for Developers:
sh
sudo snap install pieces-for-developers
Usa el código con precaución.

Paso 3: Lanzar la aplicación
Una vez completada la instalación, puedes abrir Pieces de dos maneras:
Desde la terminal, escribiendo:
sh
pieces-for-developers
ods output FailurePlot=EP_visit_plot;
Proc LifeTest Data=analysis timelist=90 180 365 730 1865 3285 
                            plots=survival(/*CB*/ failure nocensor test);
   Strata Sex_text;
   Time tt_EP*tt_EP_event(0);
run;

Data EP_visit_plot;
   Set EP_visit_plot; *reduce size*;
   if missing(_1_SURVIVAL_) and Time > 0 then delete;
run;

Proc SGPlot Data=EP_visit_plot;
   Step y=_1_SURVIVAL_  x=Time / group=stratum;
   refline 90 180 365 730 1865 3285 / axis=x;
   xaxis values=(0 90 180 365 730 1865 3285) min=0;
   yaxis max=1;
run;
quit;
<style>
  .down, .up {
    --column-height: 500px;
    --image-height: 200px;
    --row-gap: 10px;
    --num-images: 5;

    height: var(--column-height);
    overflow: hidden;
    position: relative;
    -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, black 20%, black 80%, rgba(0, 0, 0, 0) 100%);
  }

  .scroll-container {
    display: flex;
    flex-direction: column;
  }

  .up .scroll-container {
    animation: imagescrolling 15s linear infinite alternate;
    animation-delay: -0.1s; /* Offset to ensure it starts halfway through the cycle */
  }

  .down .scroll-container {
    animation: imagescrolling2 15s linear infinite alternate;
  }

  .scroll-container img {
    height: var(--image-height);
    width: 100%;
    margin-bottom: var(--row-gap);
    padding: 0;
    object-fit: cover;
  }

  @keyframes imagescrolling {
    0% {
      transform: translateY(0);
    }
    100% {
      transform: translateY(calc(
        -1 * (((var(--image-height) + var(--row-gap)) * var(--num-images)) - var(--column-height))
      ));
    }
  }

  @keyframes imagescrolling2 {
    0% {
      transform: translateY(calc(
        -1 * (((var(--image-height) + var(--row-gap)) * var(--num-images)) - var(--column-height))
      ));
    }
    100% {
      transform: translateY(0);
    }
  }
</style>
laravel:
composer create-project laravel/laravel nombre-proyecto
yii2:
composer create-project --prefer-dist yiisoft/yii2-app-basic basic
composer create-project --prefer-dist yiisoft/yii2-app-advanced yii-application

yii3
composer create-project yiisoft/app nombre-del-proyecto

stos comandos instalan las versiones estables más recientes y las crean listas para usar. Si se desea la última versión de desarrollo, se puede añadir la opción --stability=dev, aunque no es recomendado para producción.

La opción --stability=dev se coloca directamente en el comando de Composer al momento de crear el proyecto con create-project. Sirve para indicar que deseas instalar la versión de desarrollo (inestable) de un paquete en lugar de la versión estable por defecto.
Cómo y dónde colocar --stability=dev

Ejemplo para Yii2 Basic con la versión de desarrollo:

composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic
Ejemplo para Yii2 Advanced con la versión de desarrollo:

composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-advanced yii-application
Ejemplo para Yii3 con la versión de desarrollo:

composer create-project --stability=dev yiisoft/app nombre-del-proyecto
Detalles importantes
--stability=dev va después de create-project y antes del nombre del paquete o justo junto con la opción --prefer-dist.

Esta opción indica a Composer que acepte versiones con estabilidad "dev" (desarrollo), que pueden incluir cambios recientes no testeados para producción.

Si utilizas esta opción, es posible que debas también agregar --prefer-source para que Composer use el código fuente en lugar de los paquetes comprimidos.
var $sticky = $(".sticky-column").stickit({				
  scope: StickScope.Parent,				
  top: 20,
  screenMinWidth: 980
});	

$sticky.bind('stickit:end', function () {
  $('.sticky-column').addClass('bottom-0');
});
$sticky.bind('stickit:stick', function () {
  $('.sticky-column').removeClass('bottom-0');
});

https://github.com/emn178/jquery-stickit
public static function Lista()
    {
        $s = \yii\helpers\ArrayHelper::map(Transportes::find()->orderBy('id_transporte')->all(), 'id_transporte', 'desc_transporte');
        return ($s) ? $s : [];
    }
public void reverseString(char[] s) {
  char[] fixedS = s.clone();

  for (int i = 0; i < s.length; i++) {
    s[i] = fixedS[(fixedS.length - 1) - i];
  }
}
public int singleNumber(int[] nums) {
  Arrays.sort(nums);

  for (int i = 0; i < (nums.length - 1); i++) {

    if ((i == 0 && nums[i + 1] != nums[i])
        || (nums[i + 1] != nums[i] && nums[i] != nums[i - 1])){
      return nums[i];
    }
  }
  return nums[nums.length - 1];
}
tablas a:
-- Table: public.tipobuques

-- DROP TABLE IF EXISTS public.tipobuques;

CREATE TABLE IF NOT EXISTS public.tipobuques
(
    id_tipo integer NOT NULL DEFAULT nextval('tipo_buques_id_tipo_seq'::regclass),
    desc_tipo character varying COLLATE pg_catalog."default",
    otros_datos character varying(255) COLLATE pg_catalog."default",
    CONSTRAINT tipo_buques_pkey PRIMARY KEY (id_tipo)
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public.tipobuques
    OWNER to postgres;

tabla b:
-- Table: public.transportes

-- DROP TABLE IF EXISTS public.transportes;

CREATE TABLE IF NOT EXISTS public.transportes
(
    id_transporte integer NOT NULL DEFAULT nextval('transportes_id_transporte_seq'::regclass),
    nombre_transporte character varying(255) COLLATE pg_catalog."default" NOT NULL,
    id_buquescarga integer,
    id_tipo integer[],
    id_nacionalidad integer,
    otros_datos character varying(255) COLLATE pg_catalog."default",
    CONSTRAINT transportes_pkey PRIMARY KEY (id_transporte)
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public.transportes
    OWNER to postgres;

<div class="col-xs-4" id='id_transporte' style="display:none;">
                <?= $form->field($model, 'id_transporte')
                    ->dropDownList(
                        ArrayHelper::map(
                            Transportes::find()->all(),
                            'id_transporte',
                            'nombre_transporte'
                        ),
                        [
                            'prompt' => 'Seleccione Transporte',
                            'id' => 'id-transporte'
                        ]
                    ); ?>
            </div>

            <div class="col-xs-4" id='id_tipo' style="display:none;">
                <?= $form->field($model, 'id_tipo')->widget(\kartik\depdrop\DepDrop::classname(), [
                    'options' => ['id' => 'id-tipo'],
                    'pluginOptions' => [
                        'depends' => ['id-transporte'],
                        'placeholder' => 'Seleccione Tipo de Buque',
                        'url' => \yii\helpers\Url::to(['transportes/listartipos'])
                    ],
                ]); ?>
            </div>
functionController:
public function actionListartipos()
{
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    
    $out = [];
    
    if (isset($_POST['depdrop_parents'])) {
        $id_transporte = $_POST['depdrop_parents'][0];
        
        if (!empty($id_transporte)) {
            // Buscar el transporte seleccionado
            $transporte = \app\models\Transportes::findOne($id_transporte);
            
            if ($transporte && !empty($transporte->id_tipo)) {
                // Convertir el array PostgreSQL a un array de PHP
                $ids_tipos = $transporte->id_tipo;
                
                // Si es una cadena (formato PostgreSQL array), convertir a array
                if (is_string($ids_tipos)) {
                    // Remover llaves y convertir a array
                    $ids_tipos = trim($ids_tipos, '{}');
                    $ids_tipos = $ids_tipos ? explode(',', $ids_tipos) : [];
                }
                
                if (!empty($ids_tipos)) {
                    // Buscar los tipos de buques correspondientes a los IDs en el array
                    $out = \app\models\Tipobuques::find()
                        ->where(['id_tipo' => $ids_tipos])
                        ->select(['id_tipo as id', 'desc_tipo as name'])
                        ->asArray()
                        ->all();
                }
            }
        }
    }
    
    return ['output' => $out, 'selected' => ''];
}
use kartik\select2\Select2;

<?= $form->field($model, 'id_tipo')->widget(Select2::classname(), [
    'data' => ArrayHelper::map(Tipobuques::find()->all(), 'id_tipo', 'desc_tipo'),
    'options' => [
        'multiple' => true,
        'placeholder' => 'Seleccione Tipos de Buque...'
    ],
]); ?>
  
index:
[
  'attribute' => 'id_tipo',
  'label' => 'Tipos de Buques',
  'value' => function ($model) {
    // Usar el método de debug seguro
    return $model->getTiposBuquesNombres();
  },
  'format' => 'raw',
],
view:
[
  'attribute' => 'id_tipo',
  'label' => 'Tipos de Buques',
  'value' => function ($model) {
    // Usar el método de debug seguro
    return $model->getTiposBuquesNombres();
  },
    'format' => 'raw',
      ],
public boolean containsDuplicate(int[] nums) {

  Set<Integer> set = new HashSet<>();

  for (int num : nums) {
    if (!set.add(num)) {
      return true;
    }
  }
  return false;
}
public void rotate(int[]nums, int k) {
  int n = nums.length;
  int[] newNums = new int[n];
  k = k % n;// k is number of rotations
  
  for(int i = 0; i < n; i++) {
    newNums[(i + k) % n] = nums[i];
  }
  
  for(int i = 0; i < n; i++){
    nums[1] = newNums[i];
  }
}
.wcgs-grid-template.wcgs-grid-height-auto.grid.wcgs-grid-template > *:only-child {
  grid-column: 1 / -1;
}
 
document.addEventListener("DOMContentLoaded", function() {
        var mediaQuery = window.matchMedia("(min-width: 641px)");
 
        function handleChange(e) {
            if (e.matches) {
                var el = document.querySelector(".fusion-no-medium-visibility.fusion-no-large-visibility #wpgs-gallery");
                if (el) {
                    el.remove();
                }
            }
        }
 
        // Run once
        handleChange(mediaQuery);
 
        // Run when screen crosses 641px breakpoint
        mediaQuery.addEventListener("change", handleChange);
});
DO $$
BEGIN
   IF NOT EXISTS (
      SELECT 1 
      FROM pg_class c
      JOIN pg_namespace n ON n.oid = c.relnamespace
      WHERE c.relname = 'carrier_id_seq'
        AND n.nspname = 'lookup'
   ) THEN
      CREATE SEQUENCE lookup.carrier_id_seq OWNED BY lookup.carrier.id;
   END IF;
END$$;

-- Make id use sequence
ALTER TABLE lookup.carrier ALTER COLUMN id SET DEFAULT nextval('lookup.carrier_id_seq');

-- Reset sequence based on max(id) from lookup.carrier
SELECT setval(
   'lookup.carrier_id_seq',
   (SELECT COALESCE(MAX(id), 0) + 1 FROM lookup.carrier),
   false
);




-- Create a sequence if not exists
DO $$
BEGIN
   IF NOT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'carrier_id_seq') THEN
      CREATE SEQUENCE carrier_id_seq OWNED BY carrier.id;
   END IF;
END$$;

-- Make id use sequence
ALTER TABLE carrier ALTER COLUMN id SET DEFAULT nextval('carrier_id_seq');


SELECT setval('carrier_id_seq', (SELECT COALESCE(MAX(id), 0) + 1 FROM carrier), false);
# Commands to add  Jar file to postgres
nano ~/.bashrc

# Add PostgreSQL JDBC JAR to Spark
export SPARK_CLASSPATH=$SPARK_CLASSPATH:/home/youruser/jars/postgresql-42.7.3.jar
export PYSPARK_SUBMIT_ARGS="--jars /home/youruser/jars/postgresql-42.7.3.jar pyspark-shell"

source ~/.bashrc

# Restart your terminal or Jupyter Notebook server to apply the changes.
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر بزرگ که کوچیک میشه
//             SliverAppBar(
//               expandedHeight: 200,
//               pinned: true,
//               floating: false,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 background: Container(
//                   decoration: const BoxDecoration(
//                     gradient: LinearGradient(
//                       colors: [Color(0xFF6A11CB), Color(0xFF2575FC)],
//                       begin: Alignment.topRight,
//                       end: Alignment.bottomLeft,
//                     ),
//                   ),
//                   child: Center(
//                     child: Icon(
//                       Icons.videogame_asset,
//                       size: 80,
//                       color: Colors.white.withOpacity(0.8),
//                     ),
//                   ),
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }

/**
 * !عالی
 * 
 * 
 */
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر تصویری
//             SliverAppBar(
//               expandedHeight: 220,
//               pinned: true,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 centerTitle: true,
//                 background: Image.asset(
//                   "assets/gaming_banner.jpg", // عکس بنر بزرگ
//                   fit: BoxFit.cover,
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی دقیق
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }
import 'package:flutter/material.dart';
import 'package:lottery_app/games/memory_page.dart';

class GamingPage extends StatelessWidget {
  const GamingPage({super.key});

  @override
  Widget build(BuildContext context) {
    final games = [
      {"title": "بازی حافظه", "icon": Icons.memory},
      {"title": "تاس شانس", "icon": Icons.casino},
      {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
      {"title": "چیستان", "icon": Icons.quiz},
      // {"title": "حدس کلمه", "icon": Icons.spellcheck},
      // {"title": "بازی حافظه", "icon": Icons.memory},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
    ];

    return Directionality(
      textDirection: TextDirection.rtl,
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            // 🔹 SliverAppBar با Parallax و Rounded Bottom
            SliverAppBar(
              expandedHeight: 260,
              pinned: true,
              stretch: true,
              //snap: false,
              backgroundColor: const Color.fromARGB(235, 0, 0, 0),
              flexibleSpace: FlexibleSpaceBar(
                title: const Text(
                  "🎮 بازی‌ها",
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                centerTitle: true,
                stretchModes: const [
                  StretchMode.zoomBackground,
                  StretchMode.fadeTitle,
                ],
                background: Stack(
                  fit: StackFit.expand,
                  children: [
                    // تصویر پس‌زمینه
                    ClipRRect(
                      borderRadius: const BorderRadius.only(
                        bottomLeft: Radius.circular(30),
                        bottomRight: Radius.circular(30),
                      ),
                      child: Image.asset(
                        "assets/gaming_banner.jpg",
                        fit: BoxFit.cover,
                      ),
                    ),
                    // Overlay Gradient
                    Container(
                      decoration: BoxDecoration(
                        borderRadius: const BorderRadius.only(
                          bottomLeft: Radius.circular(30),
                          bottomRight: Radius.circular(30),
                        ),
                        gradient: LinearGradient(
                          colors: [
                            Colors.black.withOpacity(0.4),
                            Colors.transparent,
                          ],
                          begin: Alignment.bottomCenter,
                          end: Alignment.topCenter,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            // 🔹 Grid بازی‌ها
            SliverPadding(
              padding: const EdgeInsets.all(16),
              sliver: SliverGrid(
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  mainAxisSpacing: 16,
                  crossAxisSpacing: 16,
                  childAspectRatio: 1, // مربع دقیق
                ),
                delegate: SliverChildBuilderDelegate((context, index) {
                  final game = games[index];
                  return _gameCard(
                    context,
                    game["title"] as String,
                    game["icon"] as IconData,
                  );
                }, childCount: games.length),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _gameCard(BuildContext context, String title, IconData icon) {
    return InkWell(
      onTap: () {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text("شروع بازی: $title")));
      },
      child: Card(
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
        elevation: 4,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(icon, size: 50, color: Colors.deepPurple),
            const SizedBox(height: 12),
            Text(
              title,
              style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
            ),
          ],
        ),
      ),
    );
  }
}
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر بزرگ که کوچیک میشه
//             SliverAppBar(
//               expandedHeight: 200,
//               pinned: true,
//               floating: false,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 background: Container(
//                   decoration: const BoxDecoration(
//                     gradient: LinearGradient(
//                       colors: [Color(0xFF6A11CB), Color(0xFF2575FC)],
//                       begin: Alignment.topRight,
//                       end: Alignment.bottomLeft,
//                     ),
//                   ),
//                   child: Center(
//                     child: Icon(
//                       Icons.videogame_asset,
//                       size: 80,
//                       color: Colors.white.withOpacity(0.8),
//                     ),
//                   ),
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }

/**
 * !عالی
 * 
 * 
 */
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر تصویری
//             SliverAppBar(
//               expandedHeight: 220,
//               pinned: true,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 centerTitle: true,
//                 background: Image.asset(
//                   "assets/gaming_banner.jpg", // عکس بنر بزرگ
//                   fit: BoxFit.cover,
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی دقیق
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }
import 'package:flutter/material.dart';
import 'package:lottery_app/games/memory_page.dart';
import 'package:lottery_app/games/slotmachine_data.dart';
import 'package:lottery_app/games/time_game_page.dart';
import 'package:lottery_app/games/tuch_game_page.dart';

class GamingPage extends StatelessWidget {
  const GamingPage({super.key});

  @override
  Widget build(BuildContext context) {
    final games = [
      {"title": "بازی حافظه", "icon": Icons.memory},
      {"title": "ماشین شانس", "icon": Icons.casino},
      {"title": "بازی تایم", "icon": Icons.timer_off_outlined},
      {"title": "بازی تعداد کلیک", "icon": Icons.touch_app},
      // {"title": "حدس کلمه", "icon": Icons.spellcheck},
      // {"title": "بازی حافظه", "icon": Icons.memory},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
    ];

    return Directionality(
      textDirection: TextDirection.rtl,
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            // 🔹 SliverAppBar با Parallax و Rounded Bottom
            SliverAppBar(
              expandedHeight: 260,
              pinned: true,
              stretch: true,
              //snap: false,
              backgroundColor: const Color.fromARGB(235, 0, 0, 0),
              flexibleSpace: FlexibleSpaceBar(
                title: const Text(
                  "🎮 بازی‌ها",
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                centerTitle: true,
                stretchModes: const [
                  StretchMode.zoomBackground,
                  StretchMode.fadeTitle,
                ],
                background: Stack(
                  fit: StackFit.expand,
                  children: [
                    // تصویر پس‌زمینه
                    ClipRRect(
                      borderRadius: const BorderRadius.only(
                        bottomLeft: Radius.circular(30),
                        bottomRight: Radius.circular(30),
                      ),
                      child: Image.asset(
                        "assets/gaming_banner.jpg",
                        fit: BoxFit.cover,
                      ),
                    ),
                    // Overlay Gradient
                    Container(
                      decoration: BoxDecoration(
                        borderRadius: const BorderRadius.only(
                          bottomLeft: Radius.circular(30),
                          bottomRight: Radius.circular(30),
                        ),
                        gradient: LinearGradient(
                          colors: [
                            Colors.black.withOpacity(0.4),
                            Colors.transparent,
                          ],
                          begin: Alignment.bottomCenter,
                          end: Alignment.topCenter,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            // 🔹 Grid بازی‌ها
            SliverPadding(
              padding: const EdgeInsets.all(16),
              sliver: SliverGrid(
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  mainAxisSpacing: 16,
                  crossAxisSpacing: 16,
                  childAspectRatio: 1, // مربع دقیق
                ),
                delegate: SliverChildBuilderDelegate((context, index) {
                  final game = games[index];
                  return _gameCard(
                    context,
                    game["title"] as String,
                    game["icon"] as IconData,
                  );
                }, childCount: games.length),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _gameCard(BuildContext context, String title, IconData icon) {
    return InkWell(
      onTap: () {
        Widget page;

        switch (title) {
          case "بازی حافظه":
            page = MemoryTilesApp(); // صفحه مخصوص شطرنج
            break;
          case "ماشین شانس":
            page = SlotMachineGame(); // صفحه مخصوص فوتبال
            break;
          case "بازی تایم":
            page = TimeGamePage(); // صفحه مخصوص پازل
            break;
          case "بازی تعداد کلیک":
            page = TouchGameApp(); // صفحه مخصوص پازل
            break;
          default:
            page = GamingPage(); // fallback در صورتی که اسم بازی ناشناخته باشه
        }

// رفتن به صفحه بعدی
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => page),
);
      },
      child: Card(
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
        elevation: 4,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(icon, size: 50, color: Colors.deepPurple),
            const SizedBox(height: 12),
            Text(
              title,
              style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
            ),
          ],
        ),
      ),
    );
  }
}
#!/bin/bash
# Jobran Rodriguez

# Definir lista de personas (en orden) con 8 nombres
persons=("Juan" "Jobran" "Luis" "Jose" "Gabriel" "Jonathan" "Brian" "Robert")

# Detectar carpeta de escritorio válida
if [ -d "$HOME/Escritorio" ]; then
    DESKTOP_DIR="$HOME/Escritorio"
elif [ -d "$HOME/Desktop" ]; then
    DESKTOP_DIR="$HOME/Desktop"
else
    DESKTOP_DIR="$HOME/Escritorio"
    mkdir -p "$DESKTOP_DIR"
    echo "No se encontró carpeta de escritorio, se ha creado '$DESKTOP_DIR'."
fi

# Crear carpeta asignacion
A="$DESKTOP_DIR/asignacion"
mkdir -p "$A"

# Definir archivo de salida
OUTPUT_FILE="$A/asignacion_caja.txt"

echo "Hola! Este script te dirá qué días te toca buscar la caja de comida este mes."
read -p "Por favor, ingresa tu nombre: " name

# Validar mes
while true; do
    read -p "Ingresa el número del mes (1-12): " month
    if [[ "$month" =~ ^[0-9]+$ ]] && (( month >= 1 && month <= 12 )); then
        # Eliminar ceros a la izquierda para cálculos
        month=$((10#$month))
        # Formatear para mostrar
        month_fmt=$(printf "%02d" $month)
        break
    else
        echo "Error: Debes ingresar un número válido entre 1 y 12 para el mes."
    fi
done

# Validar año
while true; do
    read -p "Ingresa el año (ej. 2025): " year
    if [[ "$year" =~ ^[0-9]{4}$ ]] && (( year >= 1900 && year <= 3000 )); then
        break
    else
        echo "Error: Debes ingresar un año válido de 4 dígitos (por ejemplo, 2023)."
    fi
done

# Verificar nombre con mensaje específico
index=-1
for i in "${!persons[@]}"; do
    if [[ "${persons[$i],,}" == "${name,,}" ]]; then
        index=$i
        break
    fi
done

if [ $index -eq -1 ]; then
    echo "Error: Debes ingresar un nombre válido de las personas que trabajan en la oficina de sistemas y usan el servicio del comedor."
    echo "Nombres válidos: ${persons[*]}"
    exit 1
fi

# Fecha actual (sin ceros a la izquierda para comparaciones)
current_year=$(date +%Y)
current_month=$(date +%-m)
current_day=$(date +%-d)

# Determinar si mostrar asignación individual
show_individual=1
if (( year < current_year )) || { (( year == current_year )) && (( month < current_month )); }; then
    show_individual=0
fi

# Función para obtener días del mes
days_in_month() {
    local m=$1
    local y=$2
    
    case $m in
        1|3|5|7|8|10|12) echo 31 ;;
        4|6|9|11) echo 30 ;;
        2)
            if (( (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0)) )); then
                echo 29
            else
                echo 28
            fi
            ;;
        *)
            echo 30
            ;;
    esac
}

total_days=$(days_in_month $month $year)

# Pre-calcular días laborales y asignaciones
declare -a laboral_days
declare -a asignacion_personas
declare -a day_names
laboral_count=0

for (( day=1; day<=total_days; day++ )); do
    # Formatear día con dos dígitos para fecha
    day_fmt=$(printf "%02d" $day)
    wd=$(date -d "$year-$month_fmt-$day_fmt" +%u 2>/dev/null)
    
    if [ $? -ne 0 ]; then
        echo "Error: Fecha inválida $year-$month_fmt-$day_fmt. Verifica el mes y año."
        exit 1
    fi
    
    if (( wd >= 1 && wd <= 5 )); then
        laboral_days[laboral_count]=$day
        assign_index=$(( laboral_count % ${#persons[@]} ))
        asignacion_personas[laboral_count]=${persons[$assign_index]}
        
        # Obtener nombre del día en español
        day_name=$(LC_TIME=es_ES.UTF-8 date -d "$year-$month_fmt-$day_fmt" +%A 2>/dev/null)
        day_names[laboral_count]=$day_name
        
        ((laboral_count++))
    fi
done

# Crear o sobreescribir archivo
echo "Asignación de búsqueda de caja para $month_fmt/$year" > "$OUTPUT_FILE"
echo "--------------------------------------------" >> "$OUTPUT_FILE"

assigned_days=()
current_week=""
week_started=0

# Mostrar asignación semanal por pantalla
echo
echo "📅 Asignación semanal de búsqueda de caja para $month_fmt/$year:"
echo "=========================================================="

for (( i=0; i<laboral_count; i++ )); do
    day=${laboral_days[i]}
    assigned_person=${asignacion_personas[i]}
    day_name=${day_names[i]}
    
    # Formatear día con dos dígitos
    day_fmt=$(printf "%02d" $day)
    fecha_es=$(LC_TIME=es_ES.UTF-8 date -d "$year-$month_fmt-$day_fmt" +"%A %d de %B de %Y" 2>/dev/null)
    
    if [ $? -ne 0 ]; then
        echo "Error: No se pudo obtener información de fecha para $year-$month_fmt-$day_fmt"
        continue
    fi
    
    # Determinar semana actual
    week_num=$(date -d "$year-$month_fmt-$day_fmt" +%U 2>/dev/null)
    
    # Verificar si la fecha ya pasó
    date_passed=0
    if (( year < current_year )) || \
       { (( year == current_year )) && (( month < current_month )); } || \
       { (( year == current_year )) && (( month == current_month )) && (( day < current_day )); }; then
        date_passed=1
    fi
    
    # Escribir en archivo (todas las fechas)
    echo "$fecha_es : $assigned_person" >> "$OUTPUT_FILE"
    
    # Mostrar solo asignación individual para el usuario si corresponde
    if [[ "${assigned_person,,}" == "${name,,}" && show_individual -eq 1 ]]; then
        assigned_days+=("$fecha_es")
    fi
    
    # Mostrar asignación semanal por pantalla (solo fechas futuras o actual)
    if [ $date_passed -eq 0 ]; then
        if [ "$current_week" != "$week_num" ]; then
            if [ $week_started -eq 1 ]; then
                echo "----------------------------------------------------"
            fi
            current_week=$week_num
            week_start_date=$(date -d "$year-$month_fmt-$day_fmt -$(( $(date -d "$year-$month_fmt-$day_fmt" +%u) - 1 )) days" +"%d/%m" 2>/dev/null)
            week_end_date=$(date -d "$year-$month_fmt-$day_fmt +$(( 7 - $(date -d "$year-$month_fmt-$day_fmt" +%u) )) days" +"%d/%m" 2>/dev/null)
            echo "📋 Semana del $week_start_date al $week_end_date:"
            week_started=1
        fi
        
        # Icono según el día de la semana
        case $day_name in
            lunes) icon="🔵" ;;
            martes) icon="🟢" ;;
            miércoles|miercoles) icon="🟡" ;;
            jueves) icon="🟠" ;;
            viernes) icon="🔴" ;;
            *) icon="📌" ;;
        esac
        
        # Resaltar si es el usuario actual
        if [[ "${assigned_person,,}" == "${name,,}" ]]; then
            echo "   $icon $day_name $day: 👤 **TÚ**"
        else
            echo "   $icon $day_name $day: $assigned_person"
        fi
    fi
done

if [ $week_started -eq 1 ]; then
    echo "=========================================================="
fi

# Mostrar resultados individuales al usuario
if (( show_individual == 1 )); then
    echo
    echo "¡Hola, $name! 🎉 Aquí están los días que te toca buscar la caja de comida este mes:"
    if [ ${#assigned_days[@]} -eq 0 ]; then
        echo "¡Nada esta vez! Pero recuerda estar listo para la próxima ronda. 😉"
    else
        for d in "${assigned_days[@]}"; do
            echo "  - $d"
        done
    fi
else
    echo
    echo "Tu asignación individual no se mostrará porque el mes seleccionado ($month_fmt/$year) ya pasó."
fi

echo
echo "Además, se ha creado o actualizado el archivo con la asignación completa en:"
echo "  $OUTPUT_FILE"
echo
echo "¡Gracias por colaborar con el equipo! 💪🍱"

exit 0
public int maxProfit(int[] prices) {
  int maxProfit = 0;

  for (int i = 1; i < prices.lentgh; i++) {
    if (prices[i] > prices[i -1]) {
      maxProfit += (prices[i] - prices[i -1]);
    }
  }
  return maxProfit;
}
star

Tue Oct 07 2025 16:03:21 GMT+0000 (Coordinated Universal Time)

@selmo

star

Tue Oct 07 2025 11:43:11 GMT+0000 (Coordinated Universal Time) https://maticz.com/cryptocurrency-exchange-script

@carolinemax

star

Tue Oct 07 2025 08:42:07 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Tue Oct 07 2025 08:31:47 GMT+0000 (Coordinated Universal Time) https://www.addustechnologies.com/blog/betway-clone-script

@brucebanner #betway #clone #javascript

star

Tue Oct 07 2025 03:55:40 GMT+0000 (Coordinated Universal Time)

@BryanJ22

star

Mon Oct 06 2025 15:01:25 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Mon Oct 06 2025 09:24:26 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Mon Oct 06 2025 08:28:28 GMT+0000 (Coordinated Universal Time) https://www.programiz.com/html/online-compiler/

@David_chiira #undefined

star

Sun Oct 05 2025 22:00:19 GMT+0000 (Coordinated Universal Time)

@ddover

star

Sat Oct 04 2025 15:17:58 GMT+0000 (Coordinated Universal Time)

@milliedavidson #mac #terminal #music

star

Sat Oct 04 2025 14:24:24 GMT+0000 (Coordinated Universal Time)

@usman13

star

Sat Oct 04 2025 09:13:04 GMT+0000 (Coordinated Universal Time) https://cryptiecraft.com/kucoin-clone-script/

@RileyQuinn #kucoinclone #kucoinclonesoftware #kucoincloneapp #kucoinclonescript

star

Sat Oct 04 2025 07:13:20 GMT+0000 (Coordinated Universal Time) https://www.thecryptoape.com/binance-clone-script

@Davidbrevis

star

Fri Oct 03 2025 10:49:27 GMT+0000 (Coordinated Universal Time)

@usman13

star

Fri Oct 03 2025 07:05:28 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Thu Oct 02 2025 12:27:33 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Thu Oct 02 2025 09:16:55 GMT+0000 (Coordinated Universal Time)

@Shira

star

Wed Oct 01 2025 15:09:53 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Wed Oct 01 2025 12:44:09 GMT+0000 (Coordinated Universal Time) https://cryptiecraft.com/poloniex-clone-script/

@RileyQuinn #poloniexclone #poloniexclonescript #poloniexclonesoftware #poloniexcloneapp

star

Wed Oct 01 2025 10:33:55 GMT+0000 (Coordinated Universal Time) https://alpharive.com/trading-bot-development

@alex876

star

Wed Oct 01 2025 08:09:19 GMT+0000 (Coordinated Universal Time) https://pupontech.com/hoarder-karakeep-in-casa-os/

@teressider

star

Wed Oct 01 2025 04:25:19 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Tue Sep 30 2025 13:13:40 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Tue Sep 30 2025 13:12:26 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Mon Sep 29 2025 21:16:43 GMT+0000 (Coordinated Universal Time)

@ddover

star

Mon Sep 29 2025 20:00:52 GMT+0000 (Coordinated Universal Time) https://www.youtube.com/watch?v=LEEgL8DxHUI

@emalbert #php #wordpress #elementor

star

Mon Sep 29 2025 12:51:04 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Mon Sep 29 2025 09:21:44 GMT+0000 (Coordinated Universal Time) https://medium.com/coinmonks/opensea-clone-script-for-entrepreneurs-f592a3422bf4

@LilianAnderson #openseaclone #nftmarketplacedevelopment #whitelabelnftmarketplace #startupnftsolutions #openseaclonebusiness

star

Mon Sep 29 2025 06:11:48 GMT+0000 (Coordinated Universal Time) https://www.touchcrypto.org/gamefi-development-company

@AthurLuis7801 #gamefi #gamefidevelopment #blockchaingaming #nftgaming #playtoearn #web3gaming #cryptogaming

star

Mon Sep 29 2025 04:57:52 GMT+0000 (Coordinated Universal Time) https://uphex.com/features/ai/

@melvinoleson

star

Sun Sep 28 2025 10:43:38 GMT+0000 (Coordinated Universal Time) https://www.tekrevol.com/mobile-app-development

@charlesberline #business #webdevelopment #mobileappdevelopment

star

Sat Sep 27 2025 12:48:13 GMT+0000 (Coordinated Universal Time)

@vanthien

star

Fri Sep 26 2025 17:04:25 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Fri Sep 26 2025 14:53:35 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Fri Sep 26 2025 14:03:49 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Fri Sep 26 2025 12:57:40 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Thu Sep 25 2025 08:28:58 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Wed Sep 24 2025 09:59:00 GMT+0000 (Coordinated Universal Time) https://dnevnik.ru/r/irkutsk/marks

@soid

star

Wed Sep 24 2025 09:49:14 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Wed Sep 24 2025 09:26:59 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Wed Sep 24 2025 09:25:48 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Tue Sep 23 2025 13:07:53 GMT+0000 (Coordinated Universal Time)

@Saravana_Kumar #postgres

star

Tue Sep 23 2025 13:06:42 GMT+0000 (Coordinated Universal Time)

@Saravana_Kumar #bash

star

Tue Sep 23 2025 12:32:27 GMT+0000 (Coordinated Universal Time)

@mehran

star

Tue Sep 23 2025 12:31:39 GMT+0000 (Coordinated Universal Time)

@mehran

star

Tue Sep 23 2025 12:28:50 GMT+0000 (Coordinated Universal Time)

@jrg_300i

star

Tue Sep 23 2025 11:00:24 GMT+0000 (Coordinated Universal Time) https://www.thecryptoape.com/erc20-token-development

@Davidbrevis #ethereumtoken development #erc20token development #erc20token development company

star

Tue Sep 23 2025 10:56:57 GMT+0000 (Coordinated Universal Time)

@Inescn

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension